Cleanup: Access geometry bounds more directly

More consistently return geometry bounds with the `Bounds` type that
holds the min and max in one variable. This simplifies some code and
reduces the need to initialize separate min and max variables first.
Meshes now use the same `bounds_min_max()` function as curves and
point clouds, though the wrapper mesh isn't affected yet.

The motivation is to make some of the changes for #96968 simpler.
This commit is contained in:
Hans Goudey
2023-06-16 08:08:18 -04:00
parent 2c7daa34b5
commit 6301775f48
27 changed files with 180 additions and 215 deletions

View File

@@ -258,12 +258,9 @@ class CurvesGeometry : public ::CurvesGeometry {
MutableSpan<float2> surface_uv_coords_for_write();
/**
* Calculate the largest and smallest position values, only including control points
* (rather than evaluated points). The existing values of `min` and `max` are taken into account.
*
* \return Whether there are any points. If the curve is empty, the inputs will be unaffected.
* The largest and smallest position values of evaluated points.
*/
bool bounds_min_max(float3 &min, float3 &max) const;
std::optional<Bounds<float3>> bounds_min_max() const;
private:
/* --------------------------------------------------------------------

View File

@@ -8,13 +8,12 @@
* \ingroup bke
*/
#include <atomic>
#include <iostream>
#include <mutex>
#include "BLI_bounds_types.hh"
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLI_vector_set.hh"
#include "BKE_attribute.hh"
@@ -194,7 +193,7 @@ struct GeometrySet {
*/
Vector<const GeometryComponent *> get_components_for_read() const;
bool compute_boundbox_without_instances(float3 *r_min, float3 *r_max) const;
std::optional<Bounds<float3>> compute_boundbox_without_instances() const;
friend std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set);

View File

@@ -265,7 +265,6 @@ void BKE_mesh_nomain_to_meshkey(struct Mesh *mesh_src, struct Mesh *mesh_dst, st
/* vertex level transformations & checks (no derived mesh) */
/* basic vertex data functions */
bool BKE_mesh_minmax(const struct Mesh *me, float r_min[3], float r_max[3]);
void BKE_mesh_transform(struct Mesh *me, const float mat[4][4], bool do_keys);
void BKE_mesh_translate(struct Mesh *me, const float offset[3], bool do_keys);
@@ -512,7 +511,6 @@ bool BKE_mesh_center_median(const struct Mesh *me, float r_cent[3]);
* use when we want to ignore vertex locations that don't have connected faces.
*/
bool BKE_mesh_center_median_from_polys(const struct Mesh *me, float r_cent[3]);
bool BKE_mesh_center_bounds(const struct Mesh *me, float r_cent[3]);
bool BKE_mesh_center_of_surface(const struct Mesh *me, float r_cent[3]);
/**
* \note Mesh must be manifold with consistent face-winding,

View File

@@ -192,6 +192,7 @@ void *BKE_curves_add(Main *bmain, const char *name)
BoundBox *BKE_curves_boundbox_get(Object *ob)
{
using namespace blender;
BLI_assert(ob->type == OB_CURVES);
const Curves *curves_id = static_cast<const Curves *>(ob->data);
@@ -201,17 +202,13 @@ BoundBox *BKE_curves_boundbox_get(Object *ob)
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
const blender::bke::CurvesGeometry &curves = curves_id->geometry.wrap();
float3 min(FLT_MAX);
float3 max(-FLT_MAX);
if (!curves.bounds_min_max(min, max)) {
min = float3(-1);
max = float3(1);
const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
if (const std::optional<Bounds<float3>> bounds = curves.bounds_min_max()) {
BKE_boundbox_init_from_minmax(ob->runtime.bb, bounds->min, bounds->max);
}
else {
BKE_boundbox_init_from_minmax(ob->runtime.bb, float3(-1), float3(1));
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
}
return ob->runtime.bb;

View File

@@ -1096,19 +1096,14 @@ void CurvesGeometry::transform(const float4x4 &matrix)
this->tag_positions_changed();
}
bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const
std::optional<Bounds<float3>> CurvesGeometry::bounds_min_max() const
{
if (this->points_num() == 0) {
return false;
return std::nullopt;
}
this->runtime->bounds_cache.ensure(
[&](Bounds<float3> &r_bounds) { r_bounds = *bounds::min_max(this->evaluated_positions()); });
const Bounds<float3> &bounds = this->runtime->bounds_cache.data();
min = math::min(bounds.min, min);
max = math::max(bounds.max, max);
return true;
return this->runtime->bounds_cache.data();
}
CurvesGeometry curves_copy_point_selection(

View File

@@ -33,9 +33,7 @@ TEST(curves_geometry, Empty)
{
CurvesGeometry empty(0, 0);
empty.cyclic();
float3 min;
float3 max;
EXPECT_FALSE(empty.bounds_min_max(min, max));
EXPECT_FALSE(empty.bounds_min_max());
}
TEST(curves_geometry, Move)
@@ -52,9 +50,7 @@ TEST(curves_geometry, Move)
EXPECT_EQ(curves.curve_offsets, nullptr); /* NOLINT: bugprone-use-after-move */
/* Just a basic check that the new curves work okay. */
float3 min;
float3 max;
EXPECT_TRUE(other.bounds_min_max(min, max));
EXPECT_TRUE(other.bounds_min_max());
curves = std::move(other);

View File

@@ -196,24 +196,30 @@ Vector<const GeometryComponent *> GeometrySet::get_components_for_read() const
return components;
}
bool GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_max) const
std::optional<Bounds<float3>> GeometrySet::compute_boundbox_without_instances() const
{
using namespace blender;
bool have_minmax = false;
std::optional<Bounds<float3>> bounds;
if (const PointCloud *pointcloud = this->get_pointcloud_for_read()) {
have_minmax |= pointcloud->bounds_min_max(*r_min, *r_max);
bounds = bounds::merge(bounds, pointcloud->bounds_min_max());
}
if (const Mesh *mesh = this->get_mesh_for_read()) {
have_minmax |= BKE_mesh_wrapper_minmax(mesh, *r_min, *r_max);
Bounds<float3> mesh_bounds{float3(std::numeric_limits<float>::max()),
float3(std::numeric_limits<float>::min())};
if (BKE_mesh_wrapper_minmax(mesh, mesh_bounds.min, mesh_bounds.max)) {
bounds = bounds::merge(bounds, {mesh_bounds});
}
}
if (const Volume *volume = this->get_volume_for_read()) {
have_minmax |= BKE_volume_min_max(volume, *r_min, *r_max);
Bounds<float3> volume_bounds{float3(std::numeric_limits<float>::max()),
float3(std::numeric_limits<float>::min())};
if (BKE_volume_min_max(volume, volume_bounds.min, volume_bounds.max)) {
bounds = bounds::merge(bounds, {volume_bounds});
}
}
if (const Curves *curves_id = this->get_curves_for_read()) {
const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
have_minmax |= curves.bounds_min_max(*r_min, *r_max);
bounds = bounds::merge(bounds, curves_id->geometry.wrap().bounds_min_max());
}
return have_minmax;
return bounds;
}
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)

View File

@@ -17,6 +17,7 @@
#include "BKE_material.h"
#include "BKE_object.h"
#include "BLI_bounds.hh"
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_memarena.h"
@@ -699,25 +700,21 @@ GreasePencil *BKE_grease_pencil_new_nomain()
BoundBox *BKE_grease_pencil_boundbox_get(Object *ob)
{
using namespace blender;
BLI_assert(ob->type == OB_GREASE_PENCIL);
const GreasePencil *grease_pencil = static_cast<const GreasePencil *>(ob->data);
if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
return ob->runtime.bb;
}
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
}
float3 min(FLT_MAX);
float3 max(-FLT_MAX);
if (!grease_pencil->bounds_min_max(min, max)) {
min = float3(-1);
max = float3(1);
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
if (const std::optional<Bounds<float3>> bounds = grease_pencil->bounds_min_max()) {
BKE_boundbox_init_from_minmax(ob->runtime.bb, bounds->min, bounds->max);
}
else {
BKE_boundbox_init_from_minmax(ob->runtime.bb, float3(-1), float3(1));
}
return ob->runtime.bb;
@@ -1109,21 +1106,19 @@ void GreasePencil::foreach_editable_drawing(
foreach_drawing_ex(*this, frame, EDITABLE, function);
}
bool GreasePencil::bounds_min_max(float3 &min, float3 &max) const
std::optional<blender::Bounds<blender::float3>> GreasePencil::bounds_min_max() const
{
bool found = false;
using namespace blender;
/* FIXME: this should somehow go through the visible drawings. We don't have access to the
* scene time here, so we probably need to cache the visible drawing for each layer somehow. */
std::optional<Bounds<float3>> bounds;
for (int i = 0; i < this->drawing_array_num; i++) {
GreasePencilDrawingBase *drawing_base = this->drawing_array[i];
switch (drawing_base->type) {
case GP_DRAWING: {
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
const blender::bke::CurvesGeometry &curves = drawing->geometry.wrap();
if (curves.bounds_min_max(min, max)) {
found = true;
}
const bke::CurvesGeometry &curves = drawing->geometry.wrap();
bounds = bounds::merge(bounds, curves.bounds_min_max());
break;
}
case GP_DRAWING_REFERENCE: {
@@ -1133,7 +1128,7 @@ bool GreasePencil::bounds_min_max(float3 &min, float3 &max) const
}
}
return found;
return bounds;
}
blender::Span<const blender::bke::greasepencil::Layer *> GreasePencil::layers() const

View File

@@ -244,32 +244,7 @@ MetaElem *BKE_mball_element_add(MetaBall *mb, const int type)
BoundBox *BKE_mball_boundbox_get(Object *ob)
{
BLI_assert(ob->type == OB_MBALL);
if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
return ob->runtime.bb;
}
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
}
/* Expect that this function is only called for evaluated objects. */
const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
float min[3];
float max[3];
if (mesh_eval) {
INIT_MINMAX(min, max);
if (!BKE_mesh_minmax(mesh_eval, min, max)) {
copy_v3_fl(min, -1.0f);
copy_v3_fl(max, 1.0f);
}
}
else {
copy_v3_fl(min, 0.0f);
copy_v3_fl(max, 0.0f);
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY;
BKE_object_boundbox_calc_from_evaluated_geometry(ob);
return ob->runtime.bb;
}
@@ -704,14 +679,5 @@ void BKE_mball_data_update(Depsgraph *depsgraph, Scene *scene, Object *ob)
ob->runtime.geometry_set_eval = new GeometrySet(GeometrySet::create_with_mesh(mesh));
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
}
float3 min(std::numeric_limits<float>::max());
float3 max(-std::numeric_limits<float>::max());
if (!ob->runtime.geometry_set_eval->compute_boundbox_without_instances(&min, &max)) {
min = float3(0);
max = float3(0);
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
BKE_object_boundbox_calc_from_evaluated_geometry(ob);
};

View File

@@ -1502,21 +1502,15 @@ void BKE_mesh_looptri_get_real_edges(const blender::int2 *edges,
}
}
bool BKE_mesh_minmax(const Mesh *me, float r_min[3], float r_max[3])
std::optional<blender::Bounds<blender::float3>> Mesh::bounds_min_max() const
{
using namespace blender;
if (me->totvert == 0) {
return false;
if (this->totvert == 0) {
return std::nullopt;
}
me->runtime->bounds_cache.ensure(
[me](Bounds<float3> &r_bounds) { r_bounds = *bounds::min_max(me->vert_positions()); });
const Bounds<float3> &bounds = me->runtime->bounds_cache.data();
copy_v3_v3(r_min, math::min(bounds.min, float3(r_min)));
copy_v3_v3(r_max, math::max(bounds.max, float3(r_max)));
return true;
this->runtime->bounds_cache.ensure(
[&](Bounds<float3> &r_bounds) { r_bounds = *bounds::min_max(this->vert_positions()); });
return this->runtime->bounds_cache.data();
}
void Mesh::bounds_set_eager(const blender::Bounds<float3> &bounds)

View File

@@ -276,18 +276,6 @@ bool BKE_mesh_center_median_from_polys(const Mesh *me, float r_cent[3])
return (me->totpoly != 0);
}
bool BKE_mesh_center_bounds(const Mesh *me, float r_cent[3])
{
float min[3], max[3];
INIT_MINMAX(min, max);
if (BKE_mesh_minmax(me, min, max)) {
mid_v3_v3v3(r_cent, min, max);
return true;
}
return false;
}
bool BKE_mesh_center_of_surface(const Mesh *me, float r_cent[3])
{
float poly_area;

View File

@@ -29,6 +29,7 @@
#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_math_vector.hh"
#include "BLI_task.hh"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
@@ -153,12 +154,19 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
bool BKE_mesh_wrapper_minmax(const Mesh *me, float min[3], float max[3])
{
using namespace blender;
switch (me->runtime->wrapper_type) {
case ME_WRAPPER_TYPE_BMESH:
return BKE_editmesh_cache_calc_minmax(me->edit_mesh, me->runtime->edit_data, min, max);
case ME_WRAPPER_TYPE_MDATA:
case ME_WRAPPER_TYPE_SUBD:
return BKE_mesh_minmax(me, min, max);
case ME_WRAPPER_TYPE_SUBD: {
if (const std::optional<Bounds<float3>> bounds = me->bounds_min_max()) {
copy_v3_v3(min, math::min(bounds->min, float3(min)));
copy_v3_v3(max, math::max(bounds->max, float3(max)));
return true;
}
return false;
}
}
BLI_assert_unreachable();
return false;

View File

@@ -52,6 +52,7 @@
#include "DNA_world_types.h"
#include "BLI_blenlib.h"
#include "BLI_bounds.hh"
#include "BLI_kdtree.h"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
@@ -3847,19 +3848,17 @@ void BKE_object_boundbox_calc_from_mesh(Object *ob, const Mesh *me_eval)
bool BKE_object_boundbox_calc_from_evaluated_geometry(Object *ob)
{
float3 min(FLT_MAX);
float3 max(-FLT_MAX);
using namespace blender;
std::optional<Bounds<float3>> bounds;
if (ob->runtime.geometry_set_eval) {
if (!ob->runtime.geometry_set_eval->compute_boundbox_without_instances(&min, &max)) {
min = float3(0);
max = float3(0);
}
bounds = ob->runtime.geometry_set_eval->compute_boundbox_without_instances();
}
else if (const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob)) {
if (!BKE_mesh_wrapper_minmax(mesh_eval, min, max)) {
min = float3(0);
max = float3(0);
Bounds<float3> mesh_bounds{float3(std::numeric_limits<float>::max()),
float3(std::numeric_limits<float>::min())};
if (BKE_mesh_wrapper_minmax(mesh_eval, mesh_bounds.min, mesh_bounds.max)) {
bounds = bounds::merge(bounds, {mesh_bounds});
}
}
else {
@@ -3869,8 +3868,12 @@ bool BKE_object_boundbox_calc_from_evaluated_geometry(Object *ob)
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
if (bounds) {
BKE_boundbox_init_from_minmax(ob->runtime.bb, bounds->min, bounds->max);
}
else {
BKE_boundbox_init_from_minmax(ob->runtime.bb, float3(0), float3(0));
}
ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY;

View File

@@ -263,12 +263,12 @@ void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src, PointCloud
BKE_id_free(nullptr, pointcloud_src);
}
bool PointCloud::bounds_min_max(blender::float3 &min, blender::float3 &max) const
std::optional<blender::Bounds<blender::float3>> PointCloud::bounds_min_max() const
{
using namespace blender;
using namespace blender::bke;
if (this->totpoint == 0) {
return false;
return std::nullopt;
}
this->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
const AttributeAccessor attributes = this->attributes();
@@ -281,34 +281,35 @@ bool PointCloud::bounds_min_max(blender::float3 &min, blender::float3 &max) cons
r_bounds = *bounds::min_max(positions);
}
});
const Bounds<float3> &bounds = this->runtime->bounds_cache.data();
min = math::min(bounds.min, min);
max = math::max(bounds.max, max);
return true;
return this->runtime->bounds_cache.data();
}
BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
{
using namespace blender;
BLI_assert(ob->type == OB_POINTCLOUD);
if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
return ob->runtime.bb;
}
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = static_cast<BoundBox *>(MEM_callocN(sizeof(BoundBox), "pointcloud boundbox"));
ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
}
float3 min, max;
INIT_MINMAX(min, max);
if (ob->runtime.geometry_set_eval != nullptr) {
ob->runtime.geometry_set_eval->compute_boundbox_without_instances(&min, &max);
std::optional<Bounds<float3>> bounds;
if (ob->runtime.geometry_set_eval) {
bounds = ob->runtime.geometry_set_eval->compute_boundbox_without_instances();
}
else {
const PointCloud *pointcloud = static_cast<PointCloud *>(ob->data);
pointcloud->bounds_min_max(min, max);
bounds = pointcloud->bounds_min_max();
}
if (bounds) {
BKE_boundbox_init_from_minmax(ob->runtime.bb, bounds->min, bounds->max);
}
else {
BKE_boundbox_init_from_minmax(ob->runtime.bb, float3(-1), float3(1));
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
return ob->runtime.bb;
}

View File

@@ -21,7 +21,7 @@ namespace blender::bounds {
/**
* Find the smallest and largest values element-wise in the span.
*/
template<typename T> inline std::optional<Bounds<T>> min_max(Span<T> values)
template<typename T> [[nodiscard]] inline std::optional<Bounds<T>> min_max(Span<T> values)
{
if (values.is_empty()) {
return std::nullopt;
@@ -48,7 +48,8 @@ template<typename T> inline std::optional<Bounds<T>> min_max(Span<T> values)
* first. The template type T is expected to have an addition operator implemented with RadiusT.
*/
template<typename T, typename RadiusT>
inline std::optional<Bounds<T>> min_max_with_radii(Span<T> values, Span<RadiusT> radii)
[[nodiscard]] inline std::optional<Bounds<T>> min_max_with_radii(Span<T> values,
Span<RadiusT> radii)
{
BLI_assert(values.size() == radii.size());
if (values.is_empty()) {
@@ -72,4 +73,25 @@ inline std::optional<Bounds<T>> min_max_with_radii(Span<T> values, Span<RadiusT>
});
}
template<typename T> [[nodiscard]] inline Bounds<T> merge(const Bounds<T> &a, const Bounds<T> &b)
{
return {math::min(a.min, b.min), math::max(a.max, b.max)};
}
template<typename T>
[[nodiscard]] inline std::optional<Bounds<T>> merge(const std::optional<Bounds<T>> &a,
const std::optional<Bounds<T>> &b)
{
if (a.has_value() && b.has_value()) {
return merge(*a, *b);
}
if (a.has_value()) {
return a;
}
if (b.has_value()) {
return b;
}
return std::nullopt;
}
} // namespace blender::bounds

View File

@@ -1444,7 +1444,9 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
BKE_mesh_center_of_volume(me, cent);
}
else if (around == V3D_AROUND_CENTER_BOUNDS) {
BKE_mesh_center_bounds(me, cent);
if (const std::optional<Bounds<float3>> bounds = me->bounds_min_max()) {
cent = math::midpoint(bounds->min, bounds->max);
}
}
else { /* #V3D_AROUND_CENTER_MEDIAN. */
BKE_mesh_center_median(me, cent);
@@ -1696,11 +1698,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
/* done */
}
else if (around == V3D_AROUND_CENTER_BOUNDS) {
float3 min(std::numeric_limits<float>::max());
float3 max(-std::numeric_limits<float>::max());
if (curves.bounds_min_max(min, max)) {
cent = math::midpoint(min, max);
}
const Bounds<float3> bounds = *curves.bounds_min_max();
cent = math::midpoint(bounds.min, bounds.max);
}
else if (around == V3D_AROUND_CENTER_MEDIAN) {
cent = calculate_mean(curves.positions());
@@ -1729,10 +1728,8 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
/* 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);
if (const std::optional<Bounds<float3>> bounds = pointcloud.bounds_min_max()) {
cent = math::midpoint(bounds->min, bounds->max);
}
}
else if (around == V3D_AROUND_CENTER_MEDIAN) {

View File

@@ -15,6 +15,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_math_vector.hh"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@@ -792,21 +793,20 @@ void PAINT_OT_sample_color(wmOperatorType *ot)
/** \name Texture Paint Toggle Operator
* \{ */
static void paint_init_pivot_mesh(Object *ob, float location[3])
static blender::float3 paint_init_pivot_mesh(Object *ob)
{
using namespace blender;
const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob);
if (!me_eval) {
me_eval = (const Mesh *)ob->data;
}
float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX}, max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
if (!BKE_mesh_minmax(me_eval, min, max)) {
zero_v3(location);
zero_v3(max);
const std::optional<Bounds<float3>> bounds = me_eval->bounds_min_max();
if (!bounds) {
return float3(0.0f);
}
interp_v3_v3v3(location, min, max, 0.5f);
return math::midpoint(bounds->min, bounds->max);
}
static void paint_init_pivot_curves(Object *ob, float location[3])
@@ -824,11 +824,11 @@ static void paint_init_pivot_grease_pencil(Object *ob, float location[3])
void paint_init_pivot(Object *ob, Scene *scene)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
float location[3];
blender::float3 location;
switch (ob->type) {
case OB_MESH:
paint_init_pivot_mesh(ob, location);
location = paint_init_pivot_mesh(ob);
break;
case OB_CURVES:
paint_init_pivot_curves(ob, location);

View File

@@ -2413,14 +2413,13 @@ static bool lineart_geometry_check_visible(double model_view_proj[4][4],
double shift_y,
Mesh *use_mesh)
{
using namespace blender;
if (!use_mesh) {
return false;
}
float mesh_min[3], mesh_max[3];
INIT_MINMAX(mesh_min, mesh_max);
BKE_mesh_minmax(use_mesh, mesh_min, mesh_max);
const Bounds<float3> bounds = *use_mesh->bounds_min_max();
BoundBox bb;
BKE_boundbox_init_from_minmax(&bb, mesh_min, mesh_max);
BKE_boundbox_init_from_minmax(&bb, bounds.min, bounds.max);
double co[8][4];
double tmp[3];

View File

@@ -323,13 +323,12 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
}
/* Blender grows its bounds cache to cover animated meshes, so only author once. */
float bound_min[3];
float bound_max[3];
INIT_MINMAX(bound_min, bound_max);
BKE_mesh_minmax(mesh, bound_min, bound_max);
pxr::VtArray<pxr::GfVec3f> extent{pxr::GfVec3f{bound_min[0], bound_min[1], bound_min[2]},
pxr::GfVec3f{bound_max[0], bound_max[1], bound_max[2]}};
usd_mesh.CreateExtentAttr().Set(extent);
if (const std::optional<Bounds<float3>> bounds = mesh->bounds_min_max()) {
pxr::VtArray<pxr::GfVec3f> extent{
pxr::GfVec3f{bounds->min[0], bounds->min[1], bounds->min[2]},
pxr::GfVec3f{bounds->max[0], bounds->max[1], bounds->max[2]}};
usd_mesh.CreateExtentAttr().Set(extent);
}
}
static void get_vertices(const Mesh *mesh, USDMeshData &usd_mesh_data)

View File

@@ -13,6 +13,7 @@
#include "DNA_listBase.h"
#ifdef __cplusplus
# include "BLI_bounds_types.hh"
# include "BLI_function_ref.hh"
# include "BLI_map.hh"
# include "BLI_math_vector_types.hh"
@@ -457,7 +458,7 @@ typedef struct GreasePencil {
void foreach_editable_drawing(int frame,
blender::FunctionRef<void(int, GreasePencilDrawing &)> function);
bool bounds_min_max(blender::float3 &min, blender::float3 &max) const;
std::optional<blender::Bounds<blender::float3>> bounds_min_max() const;
/* For debugging purposes. */
void print_layer_tree();

View File

@@ -17,6 +17,8 @@
/** Workaround to forward-declare C++ type in C header. */
#ifdef __cplusplus
# include <optional>
# include "BLI_bounds_types.hh"
# include "BLI_math_vector_types.hh"
# include "BLI_offset_indices.hh"
@@ -302,6 +304,12 @@ typedef struct Mesh {
*/
blender::Span<int> looptri_polys() const;
/**
* Calculate the largest and smallest position values of vertices.
* \note Does not take non-mesh data (edit mesh) into account, see #BKE_mesh_wrapper_minmax,
*/
std::optional<blender::Bounds<blender::float3>> bounds_min_max() const;
/** Set cached mesh bounds to a known-correct value to avoid their lazy calculation later on. */
void bounds_set_eager(const blender::Bounds<blender::float3> &bounds);

View File

@@ -12,6 +12,9 @@
#include "DNA_customdata_types.h"
#ifdef __cplusplus
# include <optional>
# include "BLI_bounds_types.hh"
# include "BLI_math_vector_types.hh"
# include "BLI_span.hh"
#endif
@@ -63,7 +66,7 @@ typedef struct PointCloud {
void tag_positions_changed();
void tag_radii_changed();
bool bounds_min_max(blender::float3 &min, blender::float3 &max) const;
std::optional<blender::Bounds<blender::float3>> bounds_min_max() const;
#endif
PointCloudRuntimeHandle *runtime;

View File

@@ -378,6 +378,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
const ModifierEvalContext *ctx,
Mesh *mesh)
{
using namespace blender;
if (mesh->totvert == 0) {
return mesh;
}
@@ -458,12 +459,9 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
}
if (amd->offset_type & MOD_ARR_OFF_RELATIVE) {
float min[3], max[3];
INIT_MINMAX(min, max);
BKE_mesh_minmax(mesh, min, max);
const Bounds<float3> bounds = *mesh->bounds_min_max();
for (j = 3; j--;) {
offset[3][j] += amd->scale[j] * (max[j] - min[j]);
offset[3][j] += amd->scale[j] * (bounds.max[j] - bounds.min[j]);
}
}

View File

@@ -151,18 +151,17 @@ static bool can_use_mesh_for_orco_evaluation(MeshSeqCacheModifierData *mcmd,
static Mesh *generate_bounding_box_mesh(const Mesh *org_mesh)
{
using namespace blender;
float3 min(std::numeric_limits<float>::max());
float3 max(-std::numeric_limits<float>::max());
if (!BKE_mesh_minmax(org_mesh, min, max)) {
const std::optional<Bounds<float3>> bounds = org_mesh->bounds_min_max();
if (!bounds) {
return nullptr;
}
Mesh *result = geometry::create_cuboid_mesh(max - min, 2, 2, 2);
Mesh *result = geometry::create_cuboid_mesh(bounds->max - bounds->min, 2, 2, 2);
if (org_mesh->mat) {
result->mat = static_cast<Material **>(MEM_dupallocN(org_mesh->mat));
result->totcol = org_mesh->totcol;
}
BKE_mesh_translate(result, math::midpoint(min, max), false);
BKE_mesh_translate(result, math::midpoint(bounds->min, bounds->max), false);
return result;
}

View File

@@ -306,11 +306,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
psys_sim_data_init(&sim);
if (psys->flag & (PSYS_HAIR_DONE | PSYS_KEYED) || psys->pointcache->flag & PTCACHE_BAKED) {
float min[3], max[3];
INIT_MINMAX(min, max);
BKE_mesh_minmax(mesh, min, max);
min_co = min[track];
max_co = max[track];
if (const std::optional<blender::Bounds<blender::float3>> bounds = mesh->bounds_min_max()) {
min_co = bounds->min[track];
max_co = bounds->max[track];
}
}
result = BKE_mesh_new_nomain_from_template(mesh, maxvert, maxedge, maxpoly, maxloop);

View File

@@ -71,8 +71,9 @@ static void init_dualcon_mesh(DualConInput *input, Mesh *mesh)
input->tri_stride = sizeof(MLoopTri);
input->tottri = BKE_mesh_runtime_looptri_len(mesh);
INIT_MINMAX(input->min, input->max);
BKE_mesh_minmax(mesh, input->min, input->max);
const blender::Bounds<blender::float3> bounds = *mesh->bounds_min_max();
copy_v3_v3(input->min, bounds.min);
copy_v3_v3(input->max, bounds.max);
}
/* simple structure to hold the output: a CDDM and two counters to

View File

@@ -22,16 +22,14 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Compute the min and max of all realized geometry for the two
* vector outputs, which are only meant to consider real geometry. */
float3 min = float3(FLT_MAX);
float3 max = float3(-FLT_MAX);
geometry_set.compute_boundbox_without_instances(&min, &max);
if (min == float3(FLT_MAX)) {
const std::optional<Bounds<float3>> bounds = geometry_set.compute_boundbox_without_instances();
if (!bounds) {
params.set_output("Min", float3(0));
params.set_output("Max", float3(0));
}
else {
params.set_output("Min", min);
params.set_output("Max", max);
params.set_output("Min", bounds->min);
params.set_output("Max", bounds->max);
}
/* Generate the bounding box meshes inside each unique geometry set (including individually for
@@ -39,24 +37,22 @@ static void node_geo_exec(GeoNodeExecParams params)
* repurpose the original geometry sets for the output. */
if (params.output_is_required("Bounding Box")) {
geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) {
float3 sub_min = float3(FLT_MAX);
float3 sub_max = float3(-FLT_MAX);
std::optional<Bounds<float3>> sub_bounds;
/* Reuse the min and max calculation if this is the main "real" geometry set. */
if (&sub_geometry == &geometry_set) {
sub_min = min;
sub_max = max;
sub_bounds = bounds;
}
else {
sub_geometry.compute_boundbox_without_instances(&sub_min, &sub_max);
sub_bounds = sub_geometry.compute_boundbox_without_instances();
}
if (sub_min == float3(FLT_MAX)) {
if (!sub_bounds) {
sub_geometry.remove_geometry_during_modify();
}
else {
const float3 scale = sub_max - sub_min;
const float3 center = sub_min + scale / 2.0f;
const float3 scale = sub_bounds->max - sub_bounds->min;
const float3 center = sub_bounds->min + scale / 2.0f;
Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map");
transform_mesh(*mesh, center, float3(0), float3(1));
sub_geometry.replace_mesh(mesh);