Blender V4.3
abc_writer_curves.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2016 Kévin Dietrich. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <functional>
10#include <memory>
11
12#include "abc_writer_curves.h"
14
15#include "BLI_array_utils.hh"
16#include "BLI_offset_indices.hh"
17
18#include "DNA_curve_types.h"
19#include "DNA_object_types.h"
20
22#include "BKE_curve_to_mesh.hh"
23#include "BKE_curves.hh"
24#include "BKE_lib_id.hh"
25#include "BKE_mesh.hh"
26#include "BKE_object.hh"
27
28#include "CLG_log.h"
29static CLG_LogRef LOG = {"io.alembic"};
30
31using Alembic::AbcGeom::OCompoundProperty;
32using Alembic::AbcGeom::OCurves;
33using Alembic::AbcGeom::OCurvesSchema;
34using Alembic::AbcGeom::OInt16Property;
35using Alembic::AbcGeom::ON3fGeomParam;
36using Alembic::AbcGeom::OV2fGeomParam;
37
38namespace blender::io::alembic {
39
40const std::string ABC_CURVE_RESOLUTION_U_PROPNAME("blender:resolution");
41
42static inline Imath::V3f to_yup_V3f(float3 v)
43{
44 Imath::V3f p;
45 copy_yup_from_zup(p.getValue(), v);
46 return p;
47}
48
50
52{
53 CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
54 abc_curve_ = OCurves(args_.abc_parent, args_.abc_name, timesample_index_);
55 abc_curve_schema_ = abc_curve_.getSchema();
56
57 /* TODO: Blender supports per-curve resolutions but we're only using the first curve's data
58 * here. Investigate using OInt16ArrayProperty to write out all the data but do so efficiently.
59 * e.g. Write just a single value if all curves share the same resolution etc. */
60
61 int resolution_u = 1;
62 switch (context->object->type) {
63 case OB_CURVES_LEGACY: {
64 Curve *curves_id = static_cast<Curve *>(context->object->data);
65 resolution_u = curves_id->resolu;
66 break;
67 }
68 case OB_CURVES: {
69 Curves *curves_id = static_cast<Curves *>(context->object->data);
70 const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
71 resolution_u = curves.resolution().first();
72 break;
73 }
74 }
75
76 OCompoundProperty user_props = abc_curve_schema_.getUserProperties();
77 OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME);
78 user_prop_resolu.set(resolution_u);
79}
80
81Alembic::Abc::OObject ABCCurveWriter::get_alembic_object() const
82{
83 return abc_curve_;
84}
85
86Alembic::Abc::OCompoundProperty ABCCurveWriter::abc_prop_for_custom_props()
87{
88 return abc_schema_prop_for_custom_props(abc_curve_schema_);
89}
90
92{
93 const Curves *curves_id;
94 std::unique_ptr<Curves, std::function<void(Curves *)>> converted_curves;
95
96 switch (context.object->type) {
97 case OB_CURVES_LEGACY: {
98 const Curve *legacy_curve = static_cast<Curve *>(context.object->data);
99 converted_curves = std::unique_ptr<Curves, std::function<void(Curves *)>>(
100 bke::curve_legacy_to_curves(*legacy_curve), [](Curves *c) { BKE_id_free(nullptr, c); });
101 curves_id = converted_curves.get();
102 break;
103 }
104 case OB_CURVES:
105 curves_id = static_cast<Curves *>(context.object->data);
106 break;
107 default:
109 return;
110 }
111
112 const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
113 if (curves.points_num() == 0) {
114 return;
115 }
116
117 /* Alembic only supports 1 curve type / periodicity combination per object. Enforce this here.
118 * See: Alembic source code for OCurves.h as no documentation explicitly exists for this. */
119 const std::array<int, CURVE_TYPES_NUM> &curve_type_counts = curves.curve_type_counts();
120 const int number_of_curve_types = std::count_if(curve_type_counts.begin(),
121 curve_type_counts.end(),
122 [](const int count) { return count > 0; });
123 if (number_of_curve_types > 1) {
124 CLOG_WARN(&LOG, "Cannot export mixed curve types in the same Curves object");
125 return;
126 }
127
129 CLOG_WARN(&LOG, "Cannot export mixed cyclic and non-cyclic curves in the same Curves object");
130 return;
131 }
132
133 const bool is_cyclic = curves.cyclic().first();
134 Alembic::AbcGeom::BasisType curve_basis = Alembic::AbcGeom::kNoBasis;
135 Alembic::AbcGeom::CurveType curve_type = Alembic::AbcGeom::kLinear;
136 Alembic::AbcGeom::CurvePeriodicity periodicity = is_cyclic ? Alembic::AbcGeom::kPeriodic :
137 Alembic::AbcGeom::kNonPeriodic;
138 const CurveType blender_curve_type = CurveType(curves.curve_types().first());
139 switch (blender_curve_type) {
140 case CURVE_TYPE_POLY:
141 curve_basis = Alembic::AbcGeom::kNoBasis;
142 curve_type = Alembic::AbcGeom::kLinear;
143 break;
145 curve_basis = Alembic::AbcGeom::kCatmullromBasis;
146 curve_type = Alembic::AbcGeom::kLinear;
147 break;
149 curve_basis = Alembic::AbcGeom::kBezierBasis;
150 curve_type = Alembic::AbcGeom::kCubic;
151 break;
152 case CURVE_TYPE_NURBS:
153 curve_basis = Alembic::AbcGeom::kBsplineBasis;
154 curve_type = Alembic::AbcGeom::kVariableOrder;
155 break;
156 }
157
158 std::vector<Imath::V3f> verts;
159 std::vector<int32_t> vert_counts;
160 std::vector<float> widths;
161 std::vector<float> weights;
162 std::vector<float> knots;
163 std::vector<uint8_t> orders;
164
165 const Span<float3> positions = curves.positions();
166 const Span<float> nurbs_weights = curves.nurbs_weights();
167 const VArray<int8_t> nurbs_orders = curves.nurbs_orders();
168 const bke::AttributeAccessor curve_attributes = curves.attributes();
169 const VArray<float> radii = *curve_attributes.lookup_or_default<float>(
170 "radius", bke::AttrDomain::Point, 0.01f);
171
172 vert_counts.resize(curves.curves_num());
173 const OffsetIndices points_by_curve = curves.points_by_curve();
174 if (blender_curve_type == CURVE_TYPE_BEZIER) {
175 const Span<float3> handles_l = curves.handle_positions_left();
176 const Span<float3> handles_r = curves.handle_positions_right();
177
178 for (const int i_curve : curves.curves_range()) {
179 const IndexRange points = points_by_curve[i_curve];
180 const size_t current_vert_count = verts.size();
181
182 const int start_point_index = points.first();
183 const int last_point_index = points.last();
184
185 /* Vert order in the bezier curve representation is:
186 * [
187 * control point 0(+ width), right handle 0, left handle 1,
188 * control point 1(+ width), right handle 1, left handle 2,
189 * control point 2(+ width), ...
190 * ] */
191 for (const int i_point : points.drop_back(1)) {
192 verts.push_back(to_yup_V3f(positions[i_point]));
193 widths.push_back(radii[i_point] * 2.0f);
194
195 verts.push_back(to_yup_V3f(handles_r[i_point]));
196 verts.push_back(to_yup_V3f(handles_l[i_point + 1]));
197 }
198
199 /* The last vert in the array doesn't need a right handle because the curve stops
200 * at that point. */
201 verts.push_back(to_yup_V3f(positions[last_point_index]));
202 widths.push_back(radii[last_point_index] * 2.0f);
203
204 /* If the curve is cyclic, include the right handle of the last point and the
205 * left handle of the first point. */
206 if (is_cyclic) {
207 verts.push_back(to_yup_V3f(handles_r[last_point_index]));
208 verts.push_back(to_yup_V3f(handles_l[start_point_index]));
209 }
210
211 vert_counts[i_curve] = verts.size() - current_vert_count;
212 }
213 }
214 else {
215 verts.resize(curves.points_num());
216 widths.resize(curves.points_num());
217 for (const int i_point : curves.points_range()) {
218 verts[i_point] = to_yup_V3f(positions[i_point]);
219 widths[i_point] = radii[i_point] * 2.0f;
220 }
221
222 if (blender_curve_type == CURVE_TYPE_NURBS) {
223 weights.resize(curves.points_num());
224 std::copy_n(nurbs_weights.data(), weights.size(), weights.data());
225
226 orders.resize(curves.curves_num());
227 for (const int i_curve : curves.curves_range()) {
228 orders[i_curve] = nurbs_orders[i_curve];
229 }
230 }
231
232 offset_indices::copy_group_sizes(points_by_curve, points_by_curve.index_range(), vert_counts);
233 }
234
235 Alembic::AbcGeom::OFloatGeomParam::Sample width_sample;
236 width_sample.setVals(widths);
237
238 OCurvesSchema::Sample sample(verts,
239 vert_counts,
240 curve_type,
241 periodicity,
242 width_sample,
243 OV2fGeomParam::Sample(), /* UVs */
244 ON3fGeomParam::Sample(), /* normals */
245 curve_basis,
246 weights,
247 orders,
248 knots);
249
250 update_bounding_box(context.object);
251 sample.setSelfBounds(bounding_box_);
252 abc_curve_schema_.set(sample);
253}
254
259
260Mesh *ABCCurveMeshWriter::get_export_mesh(Object *object_eval, bool &r_needsfree)
261{
262 switch (object_eval->type) {
263 case OB_CURVES_LEGACY: {
264 Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object_eval);
265 if (mesh_eval != nullptr) {
266 /* Mesh_eval only exists when generative modifiers are in use. */
267 r_needsfree = false;
268 return mesh_eval;
269 }
270
271 r_needsfree = true;
272 return BKE_mesh_new_nomain_from_curve(object_eval);
273 }
274
275 case OB_CURVES:
276 Curves *curves = static_cast<Curves *>(object_eval->data);
277 r_needsfree = true;
278 return bke::curve_to_wire_mesh(curves->geometry.wrap());
279 }
280
281 return nullptr;
282}
283
284} // namespace blender::io::alembic
Low-level operations for curves.
void BKE_id_free(Main *bmain, void *idv)
Mesh * BKE_mesh_new_nomain_from_curve(const Object *ob)
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
CurveType
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_NURBS
@ CURVE_TYPE_POLY
@ CURVE_TYPE_CATMULL_ROM
struct Curves Curves
Object is a sort of wrapper for general info.
@ OB_CURVES_LEGACY
@ OB_CURVES
static CLG_LogRef LOG
ATTR_WARN_UNUSED_RESULT const BMVert * v
constexpr const T * data() const
Definition BLI_span.hh:216
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
Alembic::Abc::OCompoundProperty abc_schema_prop_for_custom_props(T abc_schema)
const ABCWriterConstructorArgs args_
virtual void update_bounding_box(Object *object)
virtual Mesh * get_export_mesh(Object *object_eval, bool &r_needsfree) override
ABCCurveMeshWriter(const ABCWriterConstructorArgs &args)
virtual Alembic::Abc::OObject get_alembic_object() const override
ABCCurveWriter(const ABCWriterConstructorArgs &args)
virtual void create_alembic_objects(const HierarchyContext *context) override
virtual void do_write(HierarchyContext &context) override
Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override
static bool is_cyclic(const Nurb *nu)
static float verts[][3]
int count
#define LOG(severity)
Definition log.h:33
BooleanMix booleans_mix_calc(const VArray< bool > &varray, IndexRange range_to_check)
Curves * curve_legacy_to_curves(const Curve &curve_legacy)
Mesh * curve_to_wire_mesh(const CurvesGeometry &curve, const bke::AttributeFilter &attribute_filter={})
BLI_INLINE void copy_yup_from_zup(float yup[3], const float zup[3])
static Imath::V3f to_yup_V3f(float3 v)
const std::string ABC_CURVE_RESOLUTION_U_PROPNAME
void copy_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask, MutableSpan< int > sizes)
short resolu
CurvesGeometry geometry