Files
test2/source/blender/blenkernel/intern/geometry_component_curves.cc
Jacques Lucke e09ccc9b35 Core: add templated version of BKE_id_new_nomain to reduce explicit casting
This adds a version of `BKE_id_new_nomain` that takes the ID type parameter as
template argument. This allows the function the return the newly created ID with
the correct type, removing the need to use `static_cast` on the call-site.

To make this work, I added a static `id_type` member to every ID struct. This
can also be used to create a similar API for other id management functions in
future patches.

```cpp
// Old
Mesh *mesh = static_cast<Mesh *>(BKE_id_new_nomain(ID_ME, "Mesh"));

// New
Mesh *mesh = BKE_id_new_nomain<Mesh>("Mesh");
```

Pull Request: https://projects.blender.org/blender/blender/pulls/138383
2025-05-05 18:41:03 +02:00

327 lines
9.5 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_task.hh"
#include "DNA_ID_enums.h"
#include "DNA_curve_types.h"
#include "BKE_curves.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.hh"
#include "attribute_access_intern.hh"
namespace blender::bke {
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
* \{ */
CurveComponent::CurveComponent() : GeometryComponent(Type::Curve) {}
CurveComponent::CurveComponent(Curves *curve, GeometryOwnershipType ownership)
: GeometryComponent(Type::Curve), curves_(curve), ownership_(ownership)
{
}
CurveComponent::~CurveComponent()
{
this->clear();
}
GeometryComponentPtr CurveComponent::copy() const
{
CurveComponent *new_component = new CurveComponent();
if (curves_ != nullptr) {
new_component->curves_ = BKE_curves_copy_for_eval(curves_);
new_component->ownership_ = GeometryOwnershipType::Owned;
}
return GeometryComponentPtr(new_component);
}
void CurveComponent::clear()
{
BLI_assert(this->is_mutable() || this->is_expired());
if (curves_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, curves_);
}
if (curve_for_render_ != nullptr) {
/* The curve created by this component should not have any edit mode data. */
BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr);
BKE_id_free(nullptr, curve_for_render_);
curve_for_render_ = nullptr;
}
curves_ = nullptr;
}
}
bool CurveComponent::has_curves() const
{
return curves_ != nullptr;
}
void CurveComponent::replace(Curves *curves, GeometryOwnershipType ownership)
{
BLI_assert(this->is_mutable());
this->clear();
curves_ = curves;
ownership_ = ownership;
}
Curves *CurveComponent::release()
{
BLI_assert(this->is_mutable());
Curves *curves = curves_;
curves_ = nullptr;
return curves;
}
const Curves *CurveComponent::get() const
{
return curves_;
}
Curves *CurveComponent::get_for_write()
{
BLI_assert(this->is_mutable());
if (ownership_ == GeometryOwnershipType::ReadOnly) {
curves_ = BKE_curves_copy_for_eval(curves_);
ownership_ = GeometryOwnershipType::Owned;
}
return curves_;
}
bool CurveComponent::is_empty() const
{
return curves_ == nullptr;
}
bool CurveComponent::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void CurveComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
if (curves_) {
curves_ = BKE_curves_copy_for_eval(curves_);
}
ownership_ = GeometryOwnershipType::Owned;
}
}
void CurveComponent::count_memory(MemoryCounter &memory) const
{
if (curves_) {
curves_->geometry.wrap().count_memory(memory);
}
}
const Curve *CurveComponent::get_curve_for_render() const
{
if (curves_ == nullptr) {
return nullptr;
}
if (curve_for_render_ != nullptr) {
return curve_for_render_;
}
std::lock_guard lock{curve_for_render_mutex_};
if (curve_for_render_ != nullptr) {
return curve_for_render_;
}
curve_for_render_ = BKE_id_new_nomain<Curve>(nullptr);
curve_for_render_->curve_eval = curves_;
return curve_for_render_;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Curve Normals Access
* \{ */
static Array<float3> curve_normal_point_domain(const CurvesGeometry &curves)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
const OffsetIndices evaluated_points_by_curve = curves.evaluated_points_by_curve();
const VArray<int8_t> types = curves.curve_types();
const VArray<int> resolutions = curves.resolution();
const VArray<bool> curves_cyclic = curves.cyclic();
const AttributeAccessor attributes = curves.attributes();
const VArray<float3> custom_normals = *attributes.lookup_or_default<float3>(
"custom_normal", AttrDomain::Point, float3(0, 0, 1));
const Span<float3> positions = curves.positions();
const VArray<int8_t> normal_modes = curves.normal_mode();
const Span<float3> evaluated_normals = curves.evaluated_normals();
Array<float3> results(curves.points_num());
threading::parallel_for(curves.curves_range(), 128, [&](IndexRange range) {
Vector<float3> nurbs_tangents;
for (const int i_curve : range) {
const IndexRange points = points_by_curve[i_curve];
const IndexRange evaluated_points = evaluated_points_by_curve[i_curve];
MutableSpan<float3> curve_normals = results.as_mutable_span().slice(points);
switch (types[i_curve]) {
case CURVE_TYPE_CATMULL_ROM: {
const Span<float3> normals = evaluated_normals.slice(evaluated_points);
const int resolution = resolutions[i_curve];
for (const int i : IndexRange(points.size())) {
curve_normals[i] = normals[resolution * i];
}
break;
}
case CURVE_TYPE_POLY:
curve_normals.copy_from(evaluated_normals.slice(evaluated_points));
break;
case CURVE_TYPE_BEZIER: {
const Span<float3> normals = evaluated_normals.slice(evaluated_points);
curve_normals.first() = normals.first();
const Span<int> offsets = curves.bezier_evaluated_offsets_for_curve(i_curve);
for (const int i : IndexRange(points.size()).drop_front(1)) {
curve_normals[i] = normals[offsets[i]];
}
break;
}
case CURVE_TYPE_NURBS: {
/* For NURBS curves there is no obvious correspondence between specific evaluated points
* and control points, so normals are determined by treating them as poly curves. */
nurbs_tangents.clear();
nurbs_tangents.resize(points.size());
const bool cyclic = curves_cyclic[i_curve];
const Span<float3> curve_positions = positions.slice(points);
curves::poly::calculate_tangents(curve_positions, cyclic, nurbs_tangents);
switch (NormalMode(normal_modes[i_curve])) {
case NORMAL_MODE_Z_UP:
curves::poly::calculate_normals_z_up(nurbs_tangents, curve_normals);
break;
case NORMAL_MODE_MINIMUM_TWIST:
curves::poly::calculate_normals_minimum(nurbs_tangents, cyclic, curve_normals);
break;
case NORMAL_MODE_FREE:
custom_normals.materialize(points, results);
break;
}
break;
}
}
}
});
return results;
}
VArray<float3> curve_normals_varray(const CurvesGeometry &curves, const AttrDomain domain)
{
const VArray<int8_t> types = curves.curve_types();
if (curves.is_single_type(CURVE_TYPE_POLY)) {
return curves.adapt_domain<float3>(
VArray<float3>::ForSpan(curves.evaluated_normals()), AttrDomain::Point, domain);
}
Array<float3> normals = curve_normal_point_domain(curves);
if (domain == AttrDomain::Point) {
return VArray<float3>::ForContainer(std::move(normals));
}
if (domain == AttrDomain::Curve) {
return curves.adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(normals)), AttrDomain::Point, AttrDomain::Curve);
}
return nullptr;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Curve Length Field Input
* \{ */
static VArray<float> construct_curve_length_gvarray(const CurvesGeometry &curves,
const AttrDomain domain)
{
curves.ensure_evaluated_lengths();
VArray<bool> cyclic = curves.cyclic();
VArray<float> lengths = VArray<float>::ForFunc(
curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) {
return curves.evaluated_length_total_for_curve(index, cyclic[index]);
});
if (domain == AttrDomain::Curve) {
return lengths;
}
if (domain == AttrDomain::Point) {
return curves.adapt_domain<float>(std::move(lengths), AttrDomain::Curve, AttrDomain::Point);
}
return {};
}
CurveLengthFieldInput::CurveLengthFieldInput()
: CurvesFieldInput(CPPType::get<float>(), "Spline Length node")
{
category_ = Category::Generated;
}
GVArray CurveLengthFieldInput::get_varray_for_context(const CurvesGeometry &curves,
const AttrDomain domain,
const IndexMask & /*mask*/) const
{
return construct_curve_length_gvarray(curves, domain);
}
uint64_t CurveLengthFieldInput::hash() const
{
/* Some random constant hash. */
return 3549623580;
}
bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const
{
return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr;
}
std::optional<AttrDomain> CurveLengthFieldInput::preferred_domain(
const CurvesGeometry & /*curves*/) const
{
return AttrDomain::Curve;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Attribute Access
* \{ */
std::optional<AttributeAccessor> CurveComponent::attributes() const
{
return AttributeAccessor(curves_ ? &curves_->geometry : nullptr,
curves::get_attribute_accessor_functions());
}
std::optional<MutableAttributeAccessor> CurveComponent::attributes_for_write()
{
Curves *curves = this->get_for_write();
return MutableAttributeAccessor(curves ? &curves->geometry : nullptr,
curves::get_attribute_accessor_functions());
}
} // namespace blender::bke