Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_curves.h8
-rw-r--r--source/blender/blenkernel/BKE_curves.hh156
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/curves.cc171
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc377
-rw-r--r--source/blender/blenkernel/intern/object.cc2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curves.cc6
-rw-r--r--source/blender/makesdna/DNA_curves_types.h40
-rw-r--r--source/blender/makesrna/intern/rna_curves.c8
9 files changed, 651 insertions, 119 deletions
diff --git a/source/blender/blenkernel/BKE_curves.h b/source/blender/blenkernel/BKE_curves.h
index 2cce15fbfd6..88bb1c67fd1 100644
--- a/source/blender/blenkernel/BKE_curves.h
+++ b/source/blender/blenkernel/BKE_curves.h
@@ -2,9 +2,11 @@
#pragma once
+#include "DNA_curves_types.h"
+
/** \file
* \ingroup bke
- * \brief Low-level operations for curves.
+ * \brief Low-level operations for curves that cannot be defined in the C++ header yet.
*/
#ifdef __cplusplus
@@ -23,14 +25,10 @@ void *BKE_curves_add(struct Main *bmain, const char *name);
struct BoundBox *BKE_curves_boundbox_get(struct Object *ob);
-void BKE_curves_update_customdata_pointers(struct Curves *curves);
bool BKE_curves_customdata_required(struct Curves *curves, struct CustomDataLayer *layer);
/* Depsgraph */
-struct Curves *BKE_curves_new_for_eval(const struct Curves *curves_src,
- int totpoint,
- int totcurve);
struct Curves *BKE_curves_copy_for_eval(struct Curves *curves_src, bool reference);
void BKE_curves_data_update(struct Depsgraph *depsgraph,
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
new file mode 100644
index 00000000000..6bcbb0f6e66
--- /dev/null
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BKE_curves.h"
+
+/** \file
+ * \ingroup bke
+ * \brief Low-level operations for curves.
+ */
+
+#include <mutex>
+
+#include "BLI_float4x4.hh"
+#include "BLI_index_mask.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_span.hh"
+#include "BLI_task.hh"
+#include "BLI_vector.hh"
+#include "BLI_virtual_array.hh"
+
+#include "BKE_attribute_access.hh"
+
+#include "FN_generic_virtual_array.hh"
+
+namespace blender::bke {
+
+/**
+ * Contains derived data, caches, and other information not saved in files, besides a few pointers
+ * to arrays that are kept in the non-runtime struct to avoid dereferencing this whenever they are
+ * accessed.
+ */
+class CurvesGeometryRuntime {
+ public:
+ /** Cache of evaluated positions. */
+ mutable Vector<float3> evaluated_position_cache;
+ mutable std::mutex position_cache_mutex;
+ mutable bool position_cache_dirty = true;
+
+ /** Direction of the spline at each evaluated point. */
+ mutable Vector<float3> evaluated_tangents_cache;
+ mutable std::mutex tangent_cache_mutex;
+ mutable bool tangent_cache_dirty = true;
+
+ /** Normal direction vectors for each evaluated point. */
+ mutable Vector<float3> evaluated_normals_cache;
+ mutable std::mutex normal_cache_mutex;
+ mutable bool normal_cache_dirty = true;
+};
+
+/**
+ * A C++ class that wraps the DNA struct for better encapsulation and ease of use. It inherits
+ * directly from the struct rather than storing a pointer to avoid more complcated ownership
+ * handling.
+ */
+class CurvesGeometry : public ::CurvesGeometry {
+ public:
+ CurvesGeometry();
+ /**
+ * Create curves with the given size. Only the position attribute is created, along with the
+ * offsets.
+ */
+ CurvesGeometry(int point_size, int curve_size);
+ CurvesGeometry(const CurvesGeometry &other);
+ CurvesGeometry &operator=(const CurvesGeometry &other);
+ ~CurvesGeometry();
+
+ static CurvesGeometry &wrap(::CurvesGeometry &dna_struct)
+ {
+ CurvesGeometry *geometry = reinterpret_cast<CurvesGeometry *>(&dna_struct);
+ return *geometry;
+ }
+ static const CurvesGeometry &wrap(const ::CurvesGeometry &dna_struct)
+ {
+ const CurvesGeometry *geometry = reinterpret_cast<const CurvesGeometry *>(&dna_struct);
+ return *geometry;
+ }
+
+ /* --------------------------------------------------------------------
+ * Accessors.
+ */
+
+ int points_size() const;
+ int curves_size() const;
+
+ /**
+ * The total number of points in the evaluated poly curve.
+ * This can depend on the resolution attribute if it exists.
+ */
+ int evaluated_points_size() const;
+
+ /**
+ * Access a range of indices of point data for a specific curve.
+ */
+ IndexRange range_for_curve(int index) const;
+
+ /** The type (#CurveType) of each curve, or potentially a single if all are the same type. */
+ VArray<int8_t> curve_types() const;
+ /** Mutable access to curve types. Call #tag_topology_changed after changing any type. */
+ MutableSpan<int8_t> curve_types();
+
+ MutableSpan<float3> positions();
+ Span<float3> positions() const;
+
+ /**
+ * 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.
+ */
+ bool bounds_min_max(float3 &min, float3 &max) const;
+
+ /**
+ * The index of the first point in every curve. The size of this span is one larger than the
+ * number of curves. Consider using #range_for_curve rather than using the offsets directly.
+ */
+ Span<int> offsets() const;
+ MutableSpan<int> offsets();
+
+ /* --------------------------------------------------------------------
+ * Operations.
+ */
+
+ /**
+ * Change the number of elements. New values for existing attributes should be properly
+ * initialized afterwards.
+ */
+ void resize(int point_size, int curve_size);
+
+ /** Call after deforming the position attribute. */
+ void tag_positions_changed();
+ /**
+ * Call after any operation that changes the topology
+ * (number of points, evaluated points, or the total count).
+ */
+ void tag_topology_changed();
+ /** Call after changing the "tilt" or "up" attributes. */
+ void tag_normals_changed();
+
+ void translate(const float3 &translation);
+ void transform(const float4x4 &matrix);
+
+ void update_customdata_pointers();
+
+ /* --------------------------------------------------------------------
+ * Attributes.
+ */
+
+ fn::GVArray adapt_domain(const fn::GVArray &varray,
+ AttributeDomain from,
+ AttributeDomain to) const;
+};
+
+Curves *curves_new_nomain(int point_size, int curves_size);
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index bf720fa1341..78a145335b4 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -103,6 +103,7 @@ set(SRC
intern/cryptomatte.cc
intern/curve.cc
intern/curves.cc
+ intern/curves_geometry.cc
intern/curve_bevel.c
intern/curve_convert.c
intern/curve_decimate.c
@@ -341,6 +342,7 @@ set(SRC
BKE_cryptomatte.hh
BKE_curve.h
BKE_curves.h
+ BKE_curves.hh
BKE_curve_to_mesh.hh
BKE_curveprofile.h
BKE_customdata.h
diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc
index ccc20d5118a..e4a57ee1897 100644
--- a/source/blender/blenkernel/intern/curves.cc
+++ b/source/blender/blenkernel/intern/curves.cc
@@ -14,16 +14,18 @@
#include "DNA_material_types.h"
#include "DNA_object_types.h"
+#include "BLI_bounds.hh"
#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_vector.hh"
#include "BLI_rand.hh"
+#include "BLI_span.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
-#include "BKE_curves.h"
+#include "BKE_curves.hh"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_idtype.h"
@@ -44,11 +46,12 @@ using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::RandomNumberGenerator;
+using blender::Span;
static const char *ATTR_POSITION = "position";
-static const char *ATTR_RADIUS = "radius";
static void curves_random(Curves *curves);
+static void update_custom_data_pointers(Curves &curves);
static void curves_init_data(ID *id)
{
@@ -57,50 +60,38 @@ static void curves_init_data(ID *id)
MEMCPY_STRUCT_AFTER(curves, DNA_struct_default_get(Curves), id);
- CustomData_reset(&curves->geometry.point_data);
- CustomData_reset(&curves->geometry.curve_data);
-
- CustomData_add_layer_named(&curves->geometry.point_data,
- CD_PROP_FLOAT3,
- CD_CALLOC,
- nullptr,
- curves->geometry.point_size,
- ATTR_POSITION);
- CustomData_add_layer_named(&curves->geometry.point_data,
- CD_PROP_FLOAT,
- CD_CALLOC,
- nullptr,
- curves->geometry.point_size,
- ATTR_RADIUS);
-
- BKE_curves_update_customdata_pointers(curves);
+ new (&curves->geometry) blender::bke::CurvesGeometry();
curves_random(curves);
}
static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag)
{
+ using namespace blender;
+
Curves *curves_dst = (Curves *)id_dst;
const Curves *curves_src = (const Curves *)id_src;
curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat));
- curves_dst->geometry.point_size = curves_src->geometry.point_size;
- curves_dst->geometry.curve_size = curves_src->geometry.curve_size;
+ const bke::CurvesGeometry &src = bke::CurvesGeometry::wrap(curves_src->geometry);
+ bke::CurvesGeometry &dst = bke::CurvesGeometry::wrap(curves_dst->geometry);
+
+ /* We need special handling here because the generic ID management code has already done a
+ * shallow copy from the source to the destination, and because the copy-on-write functionality
+ * isn't supported more generically yet. */
+
+ dst.point_size = src.point_size;
+ dst.curve_size = src.curve_size;
const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
- CustomData_copy(&curves_src->geometry.point_data,
- &curves_dst->geometry.point_data,
- CD_MASK_ALL,
- alloc_type,
- curves_dst->geometry.point_size);
- CustomData_copy(&curves_src->geometry.curve_data,
- &curves_dst->geometry.curve_data,
- CD_MASK_ALL,
- alloc_type,
- curves_dst->geometry.curve_size);
- BKE_curves_update_customdata_pointers(curves_dst);
-
- curves_dst->geometry.offsets = static_cast<int *>(MEM_dupallocN(curves_src->geometry.offsets));
+ CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_size);
+ CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_size);
+
+ dst.curve_offsets = static_cast<int *>(MEM_dupallocN(src.curve_offsets));
+
+ dst.runtime = MEM_new<bke::CurvesGeometryRuntime>(__func__);
+
+ dst.update_customdata_pointers();
curves_dst->batch_cache = nullptr;
}
@@ -110,12 +101,9 @@ static void curves_free_data(ID *id)
Curves *curves = (Curves *)id;
BKE_animdata_free(&curves->id, false);
- BKE_curves_batch_cache_free(curves);
-
- CustomData_free(&curves->geometry.point_data, curves->geometry.point_size);
- CustomData_free(&curves->geometry.curve_data, curves->geometry.curve_size);
+ blender::bke::CurvesGeometry::wrap(curves->geometry).~CurvesGeometry();
- MEM_SAFE_FREE(curves->geometry.offsets);
+ BKE_curves_batch_cache_free(curves);
MEM_SAFE_FREE(curves->mat);
}
@@ -157,7 +145,7 @@ static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_addre
CD_MASK_ALL,
&curves->id);
- BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.offsets);
+ BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.curve_offsets);
BLO_write_pointer_array(writer, curves->totcol, curves->mat);
if (curves->adt) {
@@ -182,9 +170,11 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id)
/* Geometry */
CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size);
CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_size);
- BKE_curves_update_customdata_pointers(curves);
+ update_custom_data_pointers(*curves);
+
+ BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.curve_offsets);
- BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.offsets);
+ curves->geometry.runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__);
/* Materials */
BLO_read_pointer_array(reader, (void **)&curves->mat);
@@ -236,34 +226,33 @@ IDTypeInfo IDType_ID_CV = {
/*lib_override_apply_post */ nullptr,
};
+static void update_custom_data_pointers(Curves &curves)
+{
+ blender::bke::CurvesGeometry::wrap(curves.geometry).update_customdata_pointers();
+}
+
static void curves_random(Curves *curves)
{
- CurvesGeometry &geometry = curves->geometry;
const int numpoints = 8;
- geometry.curve_size = 500;
-
- geometry.curve_size = 500;
- geometry.point_size = geometry.curve_size * numpoints;
+ blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry);
+ geometry = blender::bke::CurvesGeometry(500 * numpoints, 500);
- curves->geometry.offsets = (int *)MEM_calloc_arrayN(
- curves->geometry.curve_size + 1, sizeof(int), __func__);
- CustomData_realloc(&geometry.point_data, geometry.point_size);
- CustomData_realloc(&geometry.curve_data, geometry.curve_size);
- BKE_curves_update_customdata_pointers(curves);
+ MutableSpan<int> offsets = geometry.offsets();
+ MutableSpan<float3> positions = geometry.positions();
- MutableSpan<int> offsets{geometry.offsets, geometry.curve_size + 1};
- MutableSpan<float3> positions{(float3 *)geometry.position, geometry.point_size};
- MutableSpan<float> radii{geometry.radius, geometry.point_size};
+ float *radius_data = (float *)CustomData_add_layer_named(
+ &geometry.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, geometry.point_size, "radius");
+ MutableSpan<float> radii{radius_data, geometry.points_size()};
for (const int i : offsets.index_range()) {
- geometry.offsets[i] = numpoints * i;
+ offsets[i] = numpoints * i;
}
RandomNumberGenerator rng;
for (int i = 0; i < geometry.curve_size; i++) {
- const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
+ const IndexRange curve_range = geometry.range_for_curve(i);
MutableSpan<float3> curve_positions = positions.slice(curve_range);
MutableSpan<float> curve_radii = radii.slice(curve_range);
@@ -304,18 +293,13 @@ BoundBox *BKE_curves_boundbox_get(Object *ob)
if (ob->runtime.bb == nullptr) {
ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
- float min[3], max[3];
- INIT_MINMAX(min, max);
-
- float(*curves_co)[3] = curves->geometry.position;
- float *curves_radius = curves->geometry.radius;
- for (int a = 0; a < curves->geometry.point_size; a++) {
- float *co = curves_co[a];
- float radius = (curves_radius) ? curves_radius[a] : 0.0f;
- const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
- const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius};
- DO_MIN(co_min, min);
- DO_MAX(co_max, max);
+ blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry);
+
+ float3 min(FLT_MAX);
+ float3 max(-FLT_MAX);
+ if (!geometry.bounds_min_max(min, max)) {
+ min = float3(-1);
+ max = float3(1);
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
@@ -324,46 +308,11 @@ BoundBox *BKE_curves_boundbox_get(Object *ob)
return ob->runtime.bb;
}
-void BKE_curves_update_customdata_pointers(Curves *curves)
-{
- curves->geometry.position = (float(*)[3])CustomData_get_layer_named(
- &curves->geometry.point_data, CD_PROP_FLOAT3, ATTR_POSITION);
- curves->geometry.radius = (float *)CustomData_get_layer_named(
- &curves->geometry.point_data, CD_PROP_FLOAT, ATTR_RADIUS);
-}
-
bool BKE_curves_customdata_required(Curves *UNUSED(curves), CustomDataLayer *layer)
{
return layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, ATTR_POSITION);
}
-/* Dependency Graph */
-
-Curves *BKE_curves_new_for_eval(const Curves *curves_src, int totpoint, int totcurve)
-{
- Curves *curves_dst = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr));
-
- STRNCPY(curves_dst->id.name, curves_src->id.name);
- curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat));
- curves_dst->totcol = curves_src->totcol;
-
- curves_dst->geometry.point_size = totpoint;
- curves_dst->geometry.curve_size = totcurve;
- CustomData_copy(&curves_src->geometry.point_data,
- &curves_dst->geometry.point_data,
- CD_MASK_ALL,
- CD_CALLOC,
- totpoint);
- CustomData_copy(&curves_src->geometry.curve_data,
- &curves_dst->geometry.curve_data,
- CD_MASK_ALL,
- CD_CALLOC,
- totcurve);
- BKE_curves_update_customdata_pointers(curves_dst);
-
- return curves_dst;
-}
-
Curves *BKE_curves_copy_for_eval(Curves *curves_src, bool reference)
{
int flags = LIB_ID_COPY_LOCALIZE;
@@ -414,7 +363,7 @@ static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph,
CD_PROP_FLOAT3,
ATTR_POSITION,
curves->geometry.point_size);
- BKE_curves_update_customdata_pointers(curves);
+ update_custom_data_pointers(*curves);
/* Created deformed coordinates array on demand. */
mti->deformVerts(
@@ -457,3 +406,15 @@ void BKE_curves_batch_cache_free(Curves *curves)
BKE_curves_batch_cache_free_cb(curves);
}
}
+
+namespace blender::bke {
+
+Curves *curves_new_nomain(const int point_size, const int curves_size)
+{
+ Curves *curves = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr));
+ CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry);
+ geometry.resize(point_size, curves_size);
+ return curves;
+}
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
new file mode 100644
index 00000000000..dcd04a408fa
--- /dev/null
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_bounds.hh"
+
+#include "DNA_curves_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
+
+namespace blender::bke {
+
+static const std::string ATTR_POSITION = "position";
+static const std::string ATTR_RADIUS = "radius";
+static const std::string ATTR_CURVE_TYPE = "curve_type";
+
+/* -------------------------------------------------------------------- */
+/** \name Constructors/Destructor
+ * \{ */
+
+CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0)
+{
+}
+
+CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size)
+{
+ this->point_size = point_size;
+ this->curve_size = curve_size;
+ CustomData_reset(&this->point_data);
+ CustomData_reset(&this->curve_data);
+
+ CustomData_add_layer_named(&this->point_data,
+ CD_PROP_FLOAT3,
+ CD_DEFAULT,
+ nullptr,
+ this->point_size,
+ ATTR_POSITION.c_str());
+
+ this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_size + 1, sizeof(int), __func__);
+
+ this->update_customdata_pointers();
+
+ this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
+}
+
+static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src)
+{
+ dst.point_size = src.point_size;
+ dst.curve_size = src.curve_size;
+ CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_size);
+ CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_size);
+
+ MEM_SAFE_FREE(dst.curve_offsets);
+ dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_size + 1, sizeof(int), __func__);
+ dst.offsets().copy_from(src.offsets());
+
+ dst.tag_topology_changed();
+
+ dst.update_customdata_pointers();
+}
+
+CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
+ : CurvesGeometry(other.point_size, other.curve_size)
+{
+ copy_curves_geometry(*this, other);
+}
+
+CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other)
+{
+ if (this != &other) {
+ copy_curves_geometry(*this, other);
+ }
+ return *this;
+}
+
+CurvesGeometry::~CurvesGeometry()
+{
+ CustomData_free(&this->point_data, this->point_size);
+ CustomData_free(&this->curve_data, this->curve_size);
+ MEM_SAFE_FREE(this->curve_offsets);
+ MEM_delete(this->runtime);
+ this->runtime = nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Accessors
+ * \{ */
+
+int CurvesGeometry::points_size() const
+{
+ return this->point_size;
+}
+int CurvesGeometry::curves_size() const
+{
+ return this->curve_size;
+}
+int CurvesGeometry::evaluated_points_size() const
+{
+ /* TODO: Implement when there are evaluated points. */
+ return 0;
+}
+
+IndexRange CurvesGeometry::range_for_curve(const int index) const
+{
+ const int offset = this->curve_offsets[index];
+ const int offset_next = this->curve_offsets[index + 1];
+ return {offset, offset_next - offset};
+}
+
+VArray<int8_t> CurvesGeometry::curve_types() const
+{
+ if (const int8_t *data = (const int8_t *)CustomData_get_layer_named(
+ &this->curve_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str())) {
+ return VArray<int8_t>::ForSpan({data, this->curve_size});
+ }
+ return VArray<int8_t>::ForSingle(CURVE_TYPE_CATMULL_ROM, this->curve_size);
+}
+
+MutableSpan<int8_t> CurvesGeometry::curve_types()
+{
+ int8_t *data = (int8_t *)CustomData_add_layer_named(&this->curve_data,
+ CD_PROP_INT8,
+ CD_CALLOC,
+ nullptr,
+ this->curve_size,
+ ATTR_CURVE_TYPE.c_str());
+ BLI_assert(data != nullptr);
+ return {data, this->curve_size};
+}
+
+MutableSpan<float3> CurvesGeometry::positions()
+{
+ CustomData_duplicate_referenced_layer(&this->point_data, CD_PROP_FLOAT3, this->point_size);
+ this->update_customdata_pointers();
+ return {(float3 *)this->position, this->point_size};
+}
+Span<float3> CurvesGeometry::positions() const
+{
+ return {(const float3 *)this->position, this->point_size};
+}
+
+MutableSpan<int> CurvesGeometry::offsets()
+{
+ return {this->curve_offsets, this->curve_size + 1};
+}
+Span<int> CurvesGeometry::offsets() const
+{
+ return {this->curve_offsets, this->curve_size + 1};
+}
+
+void CurvesGeometry::resize(const int point_size, const int curve_size)
+{
+ if (point_size != this->point_size) {
+ CustomData_realloc(&this->point_data, point_size);
+ this->point_size = point_size;
+ }
+ if (curve_size != this->curve_size) {
+ CustomData_realloc(&this->curve_data, curve_size);
+ this->curve_size = curve_size;
+ this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curve_size + 1));
+ }
+ this->tag_topology_changed();
+ this->update_customdata_pointers();
+}
+
+void CurvesGeometry::tag_positions_changed()
+{
+ this->runtime->position_cache_dirty = true;
+ this->runtime->tangent_cache_dirty = true;
+ this->runtime->normal_cache_dirty = true;
+}
+void CurvesGeometry::tag_topology_changed()
+{
+ this->runtime->position_cache_dirty = true;
+ this->runtime->tangent_cache_dirty = true;
+ this->runtime->normal_cache_dirty = true;
+}
+void CurvesGeometry::tag_normals_changed()
+{
+ this->runtime->normal_cache_dirty = true;
+}
+
+void CurvesGeometry::translate(const float3 &translation)
+{
+ MutableSpan<float3> positions = this->positions();
+ threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
+ for (float3 &position : positions.slice(range)) {
+ position += translation;
+ }
+ });
+}
+
+void CurvesGeometry::transform(const float4x4 &matrix)
+{
+ MutableSpan<float3> positions = this->positions();
+ threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
+ for (float3 &position : positions.slice(range)) {
+ position = matrix * position;
+ }
+ });
+}
+
+static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves)
+{
+ Span<float3> positions = curves.positions();
+ if (curves.radius) {
+ Span<float> radii{curves.radius, curves.points_size()};
+ return bounds::min_max_with_radii(positions, radii);
+ }
+ return bounds::min_max(positions);
+}
+
+bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const
+{
+ const std::optional<bounds::MinMaxResult<float3>> bounds = curves_bounds(*this);
+ if (!bounds) {
+ return false;
+ }
+ min = math::min(bounds->min, min);
+ max = math::max(bounds->max, max);
+ return true;
+}
+
+void CurvesGeometry::update_customdata_pointers()
+{
+ this->position = (float(*)[3])CustomData_get_layer_named(
+ &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str());
+ this->radius = (float *)CustomData_get_layer_named(
+ &this->point_data, CD_PROP_FLOAT, ATTR_RADIUS.c_str());
+ this->curve_type = (int8_t *)CustomData_get_layer_named(
+ &this->point_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Domain Interpolation
+ * \{ */
+
+/**
+ * Mix together all of a curve's control point values.
+ *
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<typename T>
+static void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves,
+ const VArray<T> &old_values,
+ MutableSpan<T> r_values)
+{
+ attribute_math::DefaultMixer<T> mixer(r_values);
+ for (const int i_curve : IndexRange(curves.curves_size())) {
+ for (const int i_point : curves.range_for_curve(i_curve)) {
+ mixer.mix_in(i_curve, old_values[i_point]);
+ }
+ }
+ mixer.finalize();
+}
+
+/**
+ * A curve is selected if all of its control points were selected.
+ *
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<>
+void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves,
+ const VArray<bool> &old_values,
+ MutableSpan<bool> r_values)
+{
+ r_values.fill(true);
+ for (const int i_curve : IndexRange(curves.curves_size())) {
+ for (const int i_point : curves.range_for_curve(i_curve)) {
+ if (!old_values[i_point]) {
+ r_values[i_curve] = false;
+ break;
+ }
+ }
+ }
+}
+
+static GVArray adapt_curve_domain_point_to_curve(const CurvesGeometry &curves,
+ const GVArray &varray)
+{
+ GVArray new_varray;
+ attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ Array<T> values(curves.curves_size());
+ adapt_curve_domain_point_to_curve_impl<T>(curves, varray.typed<T>(), values);
+ new_varray = VArray<T>::ForContainer(std::move(values));
+ }
+ });
+ return new_varray;
+}
+
+/**
+ * Copy the value from a curve to all of its points.
+ *
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<typename T>
+static void adapt_curve_domain_curve_to_point_impl(const CurvesGeometry &curves,
+ const VArray<T> &old_values,
+ MutableSpan<T> r_values)
+{
+ for (const int i_curve : IndexRange(curves.curves_size())) {
+ r_values.slice(curves.range_for_curve(i_curve)).fill(old_values[i_curve]);
+ }
+}
+
+static GVArray adapt_curve_domain_curve_to_point(const CurvesGeometry &curves,
+ const GVArray &varray)
+{
+ GVArray new_varray;
+ attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<T> values(curves.points_size());
+ adapt_curve_domain_curve_to_point_impl<T>(curves, varray.typed<T>(), values);
+ new_varray = VArray<T>::ForContainer(std::move(values));
+ });
+ return new_varray;
+}
+
+fn::GVArray CurvesGeometry::adapt_domain(const fn::GVArray &varray,
+ const AttributeDomain from,
+ const AttributeDomain to) const
+{
+ if (!varray) {
+ return {};
+ }
+ if (varray.is_empty()) {
+ return {};
+ }
+ if (from == to) {
+ return varray;
+ }
+
+ if (from == ATTR_DOMAIN_POINT && to == ATTR_DOMAIN_CURVE) {
+ return adapt_curve_domain_point_to_curve(*this, varray);
+ }
+ if (from == ATTR_DOMAIN_CURVE && to == ATTR_DOMAIN_POINT) {
+ return adapt_curve_domain_curve_to_point(*this, varray);
+ }
+
+ BLI_assert_unreachable();
+ return {};
+}
+
+/** \} */
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc
index 3a8d7d6bc8a..a381fe2c2ff 100644
--- a/source/blender/blenkernel/intern/object.cc
+++ b/source/blender/blenkernel/intern/object.cc
@@ -73,7 +73,7 @@
#include "BKE_constraint.h"
#include "BKE_crazyspace.h"
#include "BKE_curve.h"
-#include "BKE_curves.h"
+#include "BKE_curves.hh"
#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_duplilist.h"
diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc
index a779c694cd2..2153b674463 100644
--- a/source/blender/draw/intern/draw_cache_impl_curves.cc
+++ b/source/blender/draw/intern/draw_cache_impl_curves.cc
@@ -22,7 +22,7 @@
#include "DNA_curves_types.h"
#include "DNA_object_types.h"
-#include "BKE_curves.h"
+#include "BKE_curves.hh"
#include "GPU_batch.h"
#include "GPU_material.h"
@@ -133,7 +133,7 @@ static void curves_batch_cache_fill_segments_proc_pos(Curves *curves,
{
/* TODO: use hair radius layer if available. */
const int curve_size = curves->geometry.curve_size;
- Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1};
+ Span<int> offsets{curves->geometry.curve_offsets, curves->geometry.curve_size + 1};
Span<float3> positions{(float3 *)curves->geometry.position, curves->geometry.point_size};
@@ -216,7 +216,7 @@ static void curves_batch_cache_fill_strands_data(Curves *curves,
GPUVertBufRaw *seg_step)
{
const int curve_size = curves->geometry.curve_size;
- Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1};
+ Span<int> offsets{curves->geometry.curve_offsets, curves->geometry.curve_size + 1};
for (const int i : IndexRange(curve_size)) {
const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h
index 03a587c896b..c45de832e3c 100644
--- a/source/blender/makesdna/DNA_curves_types.h
+++ b/source/blender/makesdna/DNA_curves_types.h
@@ -13,6 +13,33 @@
extern "C" {
#endif
+#ifdef __cplusplus
+namespace blender::bke {
+class CurvesGeometryRuntime;
+} // namespace blender::bke
+using CurvesGeometryRuntimeHandle = blender::bke::CurvesGeometryRuntime;
+#else
+typedef struct CurvesGeometryRuntimeHandle CurvesGeometryRuntimeHandle;
+#endif
+
+typedef enum CurveType {
+ CURVE_TYPE_CATMULL_ROM = 0,
+ CURVE_TYPE_POLY = 1,
+ CURVE_TYPE_BEZIER = 2,
+ CURVE_TYPE_NURBS = 3,
+} CurveType;
+
+typedef enum HandleType {
+ /** The handle can be moved anywhere, and doesn't influence the point's other handle. */
+ BEZIER_HANDLE_FREE = 0,
+ /** The location is automatically calculated to be smooth. */
+ BEZIER_HANDLE_AUTO = 1,
+ /** The location is calculated to point to the next/previous control point. */
+ BEZIER_HANDLE_VECTOR = 2,
+ /** The location is constrained to point in the opposite direction as the other handle. */
+ BEZIER_HANDLE_ALIGN = 3,
+} HandleType;
+
/**
* A reusable data structure for geometry consisting of many curves. All control point data is
* stored contiguously for better efficiency. Data for each curve is stored as a slice of the
@@ -34,13 +61,19 @@ typedef struct CurvesGeometry {
float *radius;
/**
+ * The type of each curve. #CurveType.
+ * \note This data is owned by #curve_data.
+ */
+ int8_t *curve_type;
+
+ /**
* The start index of each curve in the point data. The size of each curve can be calculated by
* subtracting the offset from the next offset. That is valid even for the last curve because
* this array is allocated with a length one larger than the number of splines.
*
* \note This is *not* stored in #CustomData because its size is one larger than #curve_data.
*/
- int *offsets;
+ int *curve_offsets;
/**
* All attributes stored on control points (#ATTR_DOMAIN_POINT).
@@ -60,6 +93,11 @@ typedef struct CurvesGeometry {
* The number of curves in the data-block.
*/
int curve_size;
+
+ /**
+ * Runtime data for curves, stored as a pointer to allow defining this as a C++ class.
+ */
+ CurvesGeometryRuntimeHandle *runtime;
} CurvesGeometry;
typedef struct Curves {
diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c
index 8e7fb415c7d..1552a7ddbfb 100644
--- a/source/blender/makesrna/intern/rna_curves.c
+++ b/source/blender/makesrna/intern/rna_curves.c
@@ -43,7 +43,7 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter,
{
const Curves *curves = rna_curves(ptr);
rna_iterator_array_begin(iter,
- (void *)curves->geometry.offsets,
+ (void *)curves->geometry.curve_offsets,
sizeof(int),
curves->geometry.curve_size + 1,
false,
@@ -95,7 +95,7 @@ static char *rna_CurvePoint_path(PointerRNA *ptr)
static int rna_CurveSlice_index_get(PointerRNA *ptr)
{
Curves *curves = rna_curves(ptr);
- return (int)((int *)ptr->data - curves->geometry.offsets);
+ return (int)((int *)ptr->data - curves->geometry.curve_offsets);
}
static char *rna_CurveSlice_path(PointerRNA *ptr)
@@ -220,7 +220,7 @@ static void rna_def_curves(BlenderRNA *brna)
/* Point and Curve RNA API helpers. */
prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", "geometry.curve_size");
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_size");
RNA_def_property_struct_type(prop, "CurveSlice");
RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block");
@@ -243,7 +243,7 @@ static void rna_def_curves(BlenderRNA *brna)
RNA_define_verify_sdna(1);
prop = RNA_def_property(srna, "curve_offset_data", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", NULL);
+ RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", NULL);
RNA_def_property_struct_type(prop, "IntAttributeValue");
RNA_def_property_collection_funcs(prop,
"rna_Curves_curve_offset_data_begin",