Blender V4.3
usd_writer_curves.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <numeric>
6#include <string>
7
8#include <pxr/usd/usdGeom/basisCurves.h>
9#include <pxr/usd/usdGeom/curves.h>
10#include <pxr/usd/usdGeom/nurbsCurves.h>
11#include <pxr/usd/usdGeom/primvar.h>
12#include <pxr/usd/usdGeom/primvarsAPI.h>
13#include <pxr/usd/usdGeom/tokens.h>
14#include <pxr/usd/usdShade/material.h>
15#include <pxr/usd/usdShade/materialBindingAPI.h>
16
19#include "usd_utils.hh"
20#include "usd_writer_curves.hh"
21
22#include "BLI_array_utils.hh"
24#include "BLI_span.hh"
25#include "BLI_virtual_array.hh"
26
27#include "BKE_attribute.hh"
29#include "BKE_curves.hh"
30#include "BKE_lib_id.hh"
31#include "BKE_material.h"
32#include "BKE_report.hh"
33
34#include "BLT_translation.hh"
35
36#include "RNA_access.hh"
37#include "RNA_enum_types.hh"
38
39namespace blender::io::usd {
40
41pxr::UsdGeomBasisCurves USDCurvesWriter::DefineUsdGeomBasisCurves(pxr::VtValue curve_basis,
42 const bool is_cyclic,
43 const bool is_cubic) const
44{
45 pxr::UsdGeomBasisCurves basis_curves = pxr::UsdGeomBasisCurves::Define(
47 /* Not required to set the basis attribute for linear curves
48 * https://graphics.pixar.com/usd/dev/api/class_usd_geom_basis_curves.html#details */
49 if (is_cubic) {
50 basis_curves.CreateTypeAttr(pxr::VtValue(pxr::UsdGeomTokens->cubic));
51 basis_curves.CreateBasisAttr(curve_basis);
52 }
53 else {
54 basis_curves.CreateTypeAttr(pxr::VtValue(pxr::UsdGeomTokens->linear));
55 }
56
57 if (is_cyclic) {
58 basis_curves.CreateWrapAttr(pxr::VtValue(pxr::UsdGeomTokens->periodic));
59 }
60 else if (curve_basis == pxr::VtValue(pxr::UsdGeomTokens->catmullRom)) {
61 /* In Blender the first and last points are treated as endpoints. The pinned attribute tells
62 * the client that to evaluate or render the curve, it must effectively add 'phantom
63 * points' at the beginning and end of every curve in a batch. These phantom points are
64 * injected to ensure that the interpolated curve begins at P[0] and ends at P[n-1]. */
65 basis_curves.CreateWrapAttr(pxr::VtValue(pxr::UsdGeomTokens->pinned));
66 }
67 else {
68 basis_curves.CreateWrapAttr(pxr::VtValue(pxr::UsdGeomTokens->nonperiodic));
69 }
70
71 return basis_curves;
72}
73
74static void populate_curve_widths(const bke::CurvesGeometry &curves, pxr::VtArray<float> &widths)
75{
76 const bke::AttributeAccessor curve_attributes = curves.attributes();
77 const bke::AttributeReader<float> radii = curve_attributes.lookup<float>("radius",
79
80 widths.resize(radii.varray.size());
81
82 for (const int i : radii.varray.index_range()) {
83 widths[i] = radii.varray[i] * 2.0f;
84 }
85}
86
87static pxr::TfToken get_curve_width_interpolation(const pxr::VtArray<float> &widths,
88 const pxr::VtArray<int> &segments,
89 const pxr::VtIntArray &control_point_counts,
90 const bool is_cyclic,
91 ReportList *reports)
92{
93 if (widths.empty()) {
94 return pxr::TfToken();
95 }
96
97 const size_t accumulated_control_point_count = std::accumulate(
98 control_point_counts.begin(), control_point_counts.end(), 0);
99
100 /* For Blender curves, radii are always stored per point. For linear curves, this should match
101 * with USD's vertex interpolation. For cubic curves, this should match with USD's varying
102 * interpolation. */
103 if (widths.size() == accumulated_control_point_count) {
104 return pxr::UsdGeomTokens->vertex;
105 }
106
107 size_t expectedVaryingSize = std::accumulate(segments.begin(), segments.end(), 0);
108 if (!is_cyclic) {
109 expectedVaryingSize += control_point_counts.size();
110 }
111
112 if (widths.size() == expectedVaryingSize) {
113 return pxr::UsdGeomTokens->varying;
114 }
115
116 BKE_report(reports, RPT_WARNING, "Curve width size not supported for USD interpolation");
117 return pxr::TfToken();
118}
119
121 const Span<float3> positions,
122 pxr::VtArray<pxr::GfVec3f> &verts,
123 pxr::VtIntArray &control_point_counts,
124 pxr::VtArray<int> &segments,
125 const bool is_cyclic,
126 const bool is_cubic)
127{
128 const OffsetIndices points_by_curve = curves.points_by_curve();
129 for (const int i_curve : curves.curves_range()) {
130
131 const IndexRange points = points_by_curve[i_curve];
132 for (const int i_point : points) {
133 verts.push_back(
134 pxr::GfVec3f(positions[i_point][0], positions[i_point][1], positions[i_point][2]));
135 }
136
137 const int tot_points = points.size();
138 control_point_counts[i_curve] = tot_points;
139
140 /* For periodic linear curve, segment count = curveVertexCount.
141 * For periodic cubic curve, segment count = curveVertexCount / vstep.
142 * For nonperiodic linear curve, segment count = curveVertexCount - 1.
143 * For nonperiodic cubic curve, segment count = ((curveVertexCount - 4) / vstep) + 1.
144 * This function handles linear and Catmull-Rom curves. For Catmull-Rom, vstep is 1.
145 * https://graphics.pixar.com/usd/dev/api/class_usd_geom_basis_curves.html */
146 if (is_cyclic) {
147 segments[i_curve] = tot_points;
148 }
149 else if (is_cubic) {
150 segments[i_curve] = (tot_points - 4) + 1;
151 }
152 else {
153 segments[i_curve] = tot_points - 1;
154 }
155 }
156}
157
159 pxr::VtArray<pxr::GfVec3f> &verts,
160 pxr::VtIntArray &control_point_counts,
161 pxr::VtArray<float> &widths,
162 pxr::TfToken &interpolation,
163 const bool is_cyclic,
164 const bool is_cubic,
165 ReportList *reports)
166{
167 const int num_curves = curves.curve_num;
168 const Span<float3> positions = curves.positions();
169
170 pxr::VtArray<int> segments(num_curves);
171
173 curves, positions, verts, control_point_counts, segments, is_cyclic, is_cubic);
174
175 populate_curve_widths(curves, widths);
176 interpolation = get_curve_width_interpolation(
177 widths, segments, control_point_counts, is_cyclic, reports);
178}
179
181 const Span<float3> positions,
182 const Span<float3> handles_l,
183 const Span<float3> handles_r,
184 pxr::VtArray<pxr::GfVec3f> &verts,
185 pxr::VtIntArray &control_point_counts,
186 pxr::VtArray<int> &segments,
187 const bool is_cyclic)
188{
189 const int bezier_vstep = 3;
190 const OffsetIndices points_by_curve = curves.points_by_curve();
191
192 for (const int i_curve : curves.curves_range()) {
193
194 const IndexRange points = points_by_curve[i_curve];
195 const int start_point_index = points[0];
196 const int last_point_index = points[points.size() - 1];
197
198 const int start_verts_count = verts.size();
199
200 for (int i_point = start_point_index; i_point < last_point_index; i_point++) {
201
202 /* The order verts in the USD bezier curve representation is [control point 0, right handle
203 * 0, left handle 1, control point 1, right handle 1, left handle 2, control point 2, ...].
204 * The last vert in the array doesn't need a right handle because the curve stops at that
205 * point. */
206 verts.push_back(
207 pxr::GfVec3f(positions[i_point][0], positions[i_point][1], positions[i_point][2]));
208
209 const blender::float3 right_handle = handles_r[i_point];
210 verts.push_back(pxr::GfVec3f(right_handle[0], right_handle[1], right_handle[2]));
211
212 const blender::float3 left_handle = handles_l[i_point + 1];
213 verts.push_back(pxr::GfVec3f(left_handle[0], left_handle[1], left_handle[2]));
214 }
215
216 verts.push_back(pxr::GfVec3f(positions[last_point_index][0],
217 positions[last_point_index][1],
218 positions[last_point_index][2]));
219
220 /* For USD periodic bezier curves, since the curve is closed, we need to include
221 * the right handle of the last point and the left handle of the first point.
222 */
223 if (is_cyclic) {
224 const blender::float3 right_handle = handles_r[last_point_index];
225 verts.push_back(pxr::GfVec3f(right_handle[0], right_handle[1], right_handle[2]));
226
227 const blender::float3 left_handle = handles_l[start_point_index];
228 verts.push_back(pxr::GfVec3f(left_handle[0], left_handle[1], left_handle[2]));
229 }
230
231 const int tot_points = verts.size() - start_verts_count;
232 control_point_counts[i_curve] = tot_points;
233
234 if (is_cyclic) {
235 segments[i_curve] = tot_points / bezier_vstep;
236 }
237 else {
238 segments[i_curve] = ((tot_points - 4) / bezier_vstep) + 1;
239 }
240 }
241}
242
244 pxr::VtArray<pxr::GfVec3f> &verts,
245 pxr::VtIntArray &control_point_counts,
246 pxr::VtArray<float> &widths,
247 pxr::TfToken &interpolation,
248 const bool is_cyclic,
249 ReportList *reports)
250{
251 const int num_curves = curves.curve_num;
252
253 const Span<float3> positions = curves.positions();
254 const Span<float3> handles_l = curves.handle_positions_left();
255 const Span<float3> handles_r = curves.handle_positions_right();
256
257 pxr::VtArray<int> segments(num_curves);
258
260 curves, positions, handles_l, handles_r, verts, control_point_counts, segments, is_cyclic);
261
262 populate_curve_widths(curves, widths);
263 interpolation = get_curve_width_interpolation(
264 widths, segments, control_point_counts, is_cyclic, reports);
265}
266
268 pxr::VtArray<pxr::GfVec3f> &verts,
269 pxr::VtIntArray &control_point_counts,
270 pxr::VtArray<float> &widths,
271 pxr::VtArray<double> &knots,
272 pxr::VtArray<int> &orders,
273 pxr::TfToken &interpolation,
274 const bool is_cyclic)
275{
276 /* Order and range, when representing a batched NurbsCurve should be authored one value per
277 * curve. */
278 const int num_curves = curves.curve_num;
279 orders.resize(num_curves);
280
281 const Span<float3> positions = curves.positions();
282
283 VArray<int8_t> geom_orders = curves.nurbs_orders();
284 VArray<int8_t> knots_modes = curves.nurbs_knots_modes();
285
286 const OffsetIndices points_by_curve = curves.points_by_curve();
287 for (const int i_curve : curves.curves_range()) {
288 const IndexRange points = points_by_curve[i_curve];
289 for (const int i_point : points) {
290 verts.push_back(
291 pxr::GfVec3f(positions[i_point][0], positions[i_point][1], positions[i_point][2]));
292 }
293
294 const int tot_points = points.size();
295 control_point_counts[i_curve] = tot_points;
296
297 const int8_t order = geom_orders[i_curve];
298 orders[i_curve] = int(geom_orders[i_curve]);
299
300 const KnotsMode mode = KnotsMode(knots_modes[i_curve]);
301
302 const int knots_num = bke::curves::nurbs::knots_num(tot_points, order, is_cyclic);
303 Array<float> temp_knots(knots_num);
304 bke::curves::nurbs::calculate_knots(tot_points, mode, order, is_cyclic, temp_knots);
305
306 /* Knots should be the concatenation of all batched curves.
307 * https://graphics.pixar.com/usd/dev/api/class_usd_geom_nurbs_curves.html#details */
308 for (int i_knot = 0; i_knot < knots_num; i_knot++) {
309 knots.push_back(double(temp_knots[i_knot]));
310 }
311
312 /* For USD it is required to set specific end knots for periodic/non-periodic curves
313 * https://graphics.pixar.com/usd/dev/api/class_usd_geom_nurbs_curves.html#details */
314 int zeroth_knot_index = knots.size() - knots_num;
315 if (is_cyclic) {
316 knots[zeroth_knot_index] = knots[zeroth_knot_index + 1] -
317 (knots[knots.size() - 2] - knots[knots.size() - 3]);
318 knots[knots.size() - 1] = knots[knots.size() - 2] +
319 (knots[zeroth_knot_index + 2] - knots[zeroth_knot_index + 1]);
320 }
321 else {
322 knots[zeroth_knot_index] = knots[zeroth_knot_index + 1];
323 knots[knots.size() - 1] = knots[knots.size() - 2];
324 }
325 }
326
327 populate_curve_widths(curves, widths);
328 interpolation = pxr::UsdGeomTokens->vertex;
329}
330
331void USDCurvesWriter::set_writer_attributes_for_nurbs(
332 const pxr::UsdGeomNurbsCurves &usd_nurbs_curves,
333 const pxr::VtArray<double> &knots,
334 const pxr::VtArray<int> &orders,
335 const pxr::UsdTimeCode timecode)
336{
337 pxr::UsdAttribute attr_knots = usd_nurbs_curves.CreateKnotsAttr(pxr::VtValue(), true);
338 usd_value_writer_.SetAttribute(attr_knots, pxr::VtValue(knots), timecode);
339 pxr::UsdAttribute attr_order = usd_nurbs_curves.CreateOrderAttr(pxr::VtValue(), true);
340 usd_value_writer_.SetAttribute(attr_order, pxr::VtValue(orders), timecode);
341}
342
343void USDCurvesWriter::set_writer_attributes(pxr::UsdGeomCurves &usd_curves,
344 const pxr::VtArray<pxr::GfVec3f> &verts,
345 const pxr::VtIntArray &control_point_counts,
346 const pxr::VtArray<float> &widths,
347 const pxr::UsdTimeCode timecode,
348 const pxr::TfToken interpolation)
349{
350 pxr::UsdAttribute attr_points = usd_curves.CreatePointsAttr(pxr::VtValue(), true);
351 usd_value_writer_.SetAttribute(attr_points, pxr::VtValue(verts), timecode);
352
353 pxr::UsdAttribute attr_vertex_counts = usd_curves.CreateCurveVertexCountsAttr(pxr::VtValue(),
354 true);
355 usd_value_writer_.SetAttribute(attr_vertex_counts, pxr::VtValue(control_point_counts), timecode);
356
357 if (!widths.empty()) {
358 pxr::UsdAttribute attr_widths = usd_curves.CreateWidthsAttr(pxr::VtValue(), true);
359 usd_value_writer_.SetAttribute(attr_widths, pxr::VtValue(widths), timecode);
360
361 usd_curves.SetWidthsInterpolation(interpolation);
362 }
363}
364
365static std::optional<pxr::TfToken> convert_blender_domain_to_usd(
366 const bke::AttrDomain blender_domain, bool is_bezier)
367{
368 switch (blender_domain) {
370 return is_bezier ? pxr::UsdGeomTokens->varying : pxr::UsdGeomTokens->vertex;
372 return pxr::UsdGeomTokens->uniform;
373
374 default:
375 return std::nullopt;
376 }
377}
378
379void USDCurvesWriter::write_generic_data(const bke::CurvesGeometry &curves,
380 const bke::AttributeIter &attr,
381 const pxr::UsdGeomCurves &usd_curves)
382{
383 const CurveType curve_type = CurveType(curves.curve_types().first());
384 const bool is_bezier = curve_type == CURVE_TYPE_BEZIER;
385
386 const std::optional<pxr::TfToken> pv_interp = convert_blender_domain_to_usd(attr.domain,
387 is_bezier);
388 const std::optional<pxr::SdfValueTypeName> pv_type = convert_blender_type_to_usd(attr.data_type);
389
390 if (!pv_interp || !pv_type) {
391 BKE_reportf(this->reports(),
393 "Attribute '%s' (Blender domain %d, type %d) cannot be converted to USD",
394 attr.name.c_str(),
395 int8_t(attr.domain),
396 attr.data_type);
397 return;
398 }
399
400 const GVArray attribute = *attr.get();
401 if (attribute.is_empty()) {
402 return;
403 }
404
405 const pxr::UsdTimeCode timecode = get_export_time_code();
406 const pxr::TfToken pv_name(
408 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_curves);
409
410 pxr::UsdGeomPrimvar pv_attr = pv_api.CreatePrimvar(pv_name, *pv_type, *pv_interp);
411
413 attribute, attr.data_type, timecode, pv_attr, usd_value_writer_);
414}
415
416void USDCurvesWriter::write_uv_data(const bke::AttributeIter &attr,
417 const pxr::UsdGeomCurves &usd_curves)
418{
419 const VArray<float2> buffer = *attr.get<float2>(bke::AttrDomain::Curve);
420 if (buffer.is_empty()) {
421 return;
422 }
423
424 const pxr::UsdTimeCode timecode = get_export_time_code();
425 const pxr::TfToken pv_name(
427 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_curves);
428
429 pxr::UsdGeomPrimvar pv_uv = pv_api.CreatePrimvar(
430 pv_name, pxr::SdfValueTypeNames->TexCoord2fArray, pxr::UsdGeomTokens->uniform);
431
433}
434
435void USDCurvesWriter::write_custom_data(const bke::CurvesGeometry &curves,
436 const pxr::UsdGeomCurves &usd_curves)
437{
438 const bke::AttributeAccessor attributes = curves.attributes();
439
440 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
441 /* Skip "internal" Blender properties and attributes dealt with elsewhere. */
442 if (iter.name[0] == '.' || bke::attribute_name_is_anonymous(iter.name) ||
443 ELEM(iter.name,
444 "position",
445 "radius",
446 "resolution",
447 "id",
448 "curve_type",
449 "handle_left",
450 "handle_right",
451 "handle_type_left",
452 "handle_type_right"))
453 {
454 return;
455 }
456
457 /* Spline UV data */
458 if (iter.domain == bke::AttrDomain::Curve && iter.data_type == CD_PROP_FLOAT2) {
459 if (usd_export_context_.export_params.export_uvmaps) {
460 this->write_uv_data(iter, usd_curves);
461 }
462 }
463
464 /* Everything else. */
465 else {
466 this->write_generic_data(curves, iter, usd_curves);
467 }
468 });
469}
470
471void USDCurvesWriter::do_write(HierarchyContext &context)
472{
473 Curves *curves_id;
474 std::unique_ptr<Curves, std::function<void(Curves *)>> converted_curves;
475
476 switch (context.object->type) {
477 case OB_CURVES_LEGACY: {
478 const Curve *legacy_curve = static_cast<Curve *>(context.object->data);
479 converted_curves = std::unique_ptr<Curves, std::function<void(Curves *)>>(
480 bke::curve_legacy_to_curves(*legacy_curve), [](Curves *c) { BKE_id_free(nullptr, c); });
481 curves_id = converted_curves.get();
482 break;
483 }
484 case OB_CURVES:
485 curves_id = static_cast<Curves *>(context.object->data);
486 break;
487 default:
489 return;
490 }
491
492 const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
493 if (curves.points_num() == 0) {
494 return;
495 }
496
497 const std::array<int, CURVE_TYPES_NUM> &curve_type_counts = curves.curve_type_counts();
498 const int number_of_curve_types = std::count_if(curve_type_counts.begin(),
499 curve_type_counts.end(),
500 [](const int count) { return count > 0; });
501 if (number_of_curve_types > 1) {
503 reports(), RPT_WARNING, "Cannot export mixed curve types in the same Curves object");
504 return;
505 }
506
508 BKE_report(reports(),
510 "Cannot export mixed cyclic and non-cyclic curves in the same Curves object");
511 return;
512 }
513
514 const pxr::UsdTimeCode timecode = get_export_time_code();
515 const int8_t curve_type = curves.curve_types()[0];
516
517 if (first_frame_curve_type == -1) {
518 first_frame_curve_type = curve_type;
519 }
520 else if (first_frame_curve_type != curve_type) {
521 const char *first_frame_curve_type_name = nullptr;
523 rna_enum_curves_type_items, int(first_frame_curve_type), &first_frame_curve_type_name);
524
525 const char *current_curve_type_name = nullptr;
527 rna_enum_curves_type_items, int(curve_type), &current_curve_type_name);
528
529 BKE_reportf(reports(),
531 "USD does not support animating curve types. The curve type changes from %s to "
532 "%s on frame %f",
533 IFACE_(first_frame_curve_type_name),
534 IFACE_(current_curve_type_name),
535 timecode.GetValue());
536 return;
537 }
538
539 const bool is_cyclic = curves.cyclic().first();
540 pxr::VtArray<pxr::GfVec3f> verts;
541 pxr::VtIntArray control_point_counts;
542 pxr::VtArray<float> widths;
543 pxr::TfToken interpolation;
544
545 pxr::UsdGeomBasisCurves usd_basis_curves;
546 pxr::UsdGeomNurbsCurves usd_nurbs_curves;
547 pxr::UsdGeomCurves *usd_curves = nullptr;
548
549 control_point_counts.resize(curves.curves_num());
550 switch (curve_type) {
551 case CURVE_TYPE_POLY:
552 usd_basis_curves = DefineUsdGeomBasisCurves(pxr::VtValue(), is_cyclic, false);
553 usd_curves = &usd_basis_curves;
554
556 curves, verts, control_point_counts, widths, interpolation, is_cyclic, false, reports());
557 break;
559 usd_basis_curves = DefineUsdGeomBasisCurves(
560 pxr::VtValue(pxr::UsdGeomTokens->catmullRom), is_cyclic, true);
561 usd_curves = &usd_basis_curves;
562
564 curves, verts, control_point_counts, widths, interpolation, is_cyclic, true, reports());
565 break;
567 usd_basis_curves = DefineUsdGeomBasisCurves(
568 pxr::VtValue(pxr::UsdGeomTokens->bezier), is_cyclic, true);
569 usd_curves = &usd_basis_curves;
570
572 curves, verts, control_point_counts, widths, interpolation, is_cyclic, reports());
573 break;
574 case CURVE_TYPE_NURBS: {
575 pxr::VtArray<double> knots;
576 pxr::VtArray<int> orders;
577 orders.resize(curves.curves_num());
578
579 usd_nurbs_curves = pxr::UsdGeomNurbsCurves::Define(usd_export_context_.stage,
580 usd_export_context_.usd_path);
581 usd_curves = &usd_nurbs_curves;
582
584 curves, verts, control_point_counts, widths, knots, orders, interpolation, is_cyclic);
585
586 set_writer_attributes_for_nurbs(usd_nurbs_curves, knots, orders, timecode);
587
588 break;
589 }
590 default:
592 }
593
594 set_writer_attributes(*usd_curves, verts, control_point_counts, widths, timecode, interpolation);
595
596 assign_materials(context, *usd_curves);
597
598 write_custom_data(curves, *usd_curves);
599
600 auto prim = usd_curves->GetPrim();
601 write_id_properties(prim, curves_id->id, timecode);
602}
603
604void USDCurvesWriter::assign_materials(const HierarchyContext &context,
605 const pxr::UsdGeomCurves &usd_curves)
606{
607 if (context.object->totcol == 0) {
608 return;
609 }
610
611 bool curve_material_bound = false;
612 for (short mat_num = 0; mat_num < context.object->totcol; mat_num++) {
613 Material *material = BKE_object_material_get(context.object, mat_num + 1);
614 if (material == nullptr) {
615 continue;
616 }
617
618 pxr::UsdPrim curve_prim = usd_curves.GetPrim();
619 pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(curve_prim);
620 pxr::UsdShadeMaterial usd_material = ensure_usd_material(context, material);
621 api.Bind(usd_material);
622 pxr::UsdShadeMaterialBindingAPI::Apply(curve_prim);
623
624 /* USD seems to support neither per-material nor per-face-group double-sidedness, so we just
625 * use the flag from the first non-empty material slot. */
626 usd_curves.CreateDoubleSidedAttr(
627 pxr::VtValue((material->blend_flag & MA_BL_CULL_BACKFACE) == 0));
628
629 curve_material_bound = true;
630 break;
631 }
632
633 if (!curve_material_bound) {
634 /* Blender defaults to double-sided, but USD to single-sided. */
635 usd_curves.CreateDoubleSidedAttr(pxr::VtValue(true));
636 }
637}
638
639} // namespace blender::io::usd
Low-level operations for curves.
void BKE_id_free(Main *bmain, void *idv)
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define ELEM(...)
#define IFACE_(msgid)
CurveType
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_NURBS
@ CURVE_TYPE_POLY
@ CURVE_TYPE_CATMULL_ROM
struct Curves Curves
KnotsMode
@ CD_PROP_FLOAT2
@ MA_BL_CULL_BACKFACE
@ OB_CURVES_LEGACY
@ OB_CURVES
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const char * c_str() const
GAttributeReader lookup(const StringRef attribute_id) const
GAttributeReader get() const
pxr::UsdTimeCode get_export_time_code() const
pxr::UsdUtilsSparseValueWriter usd_value_writer_
const USDExporterContext usd_export_context_
static bool is_cyclic(const Nurb *nu)
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float verts[][3]
int count
BooleanMix booleans_mix_calc(const VArray< bool > &varray, IndexRange range_to_check)
void calculate_knots(int points_num, KnotsMode mode, int8_t order, bool cyclic, MutableSpan< float > knots)
int knots_num(int points_num, int8_t order, bool cyclic)
bool attribute_name_is_anonymous(const StringRef name)
Curves * curve_legacy_to_curves(const Curve &curve_legacy)
static pxr::TfToken get_curve_width_interpolation(const pxr::VtArray< float > &widths, const pxr::VtArray< int > &segments, const pxr::VtIntArray &control_point_counts, const bool is_cyclic, ReportList *reports)
static void populate_curve_props_for_nurbs(const bke::CurvesGeometry &curves, pxr::VtArray< pxr::GfVec3f > &verts, pxr::VtIntArray &control_point_counts, pxr::VtArray< float > &widths, pxr::VtArray< double > &knots, pxr::VtArray< int > &orders, pxr::TfToken &interpolation, const bool is_cyclic)
static void populate_curve_props_for_bezier(const bke::CurvesGeometry &curves, pxr::VtArray< pxr::GfVec3f > &verts, pxr::VtIntArray &control_point_counts, pxr::VtArray< float > &widths, pxr::TfToken &interpolation, const bool is_cyclic, ReportList *reports)
std::string make_safe_name(const std::string &name, bool allow_unicode)
Definition usd_utils.cc:16
void copy_blender_attribute_to_primvar(const GVArray &attribute, const eCustomDataType data_type, const pxr::UsdTimeCode timecode, const pxr::UsdGeomPrimvar &primvar, pxr::UsdUtilsSparseValueWriter &value_writer)
static std::optional< pxr::TfToken > convert_blender_domain_to_usd(const bke::AttrDomain blender_domain, bool is_bezier)
static void populate_curve_verts(const bke::CurvesGeometry &curves, const Span< float3 > positions, pxr::VtArray< pxr::GfVec3f > &verts, pxr::VtIntArray &control_point_counts, pxr::VtArray< int > &segments, const bool is_cyclic, const bool is_cubic)
static void populate_curve_props(const bke::CurvesGeometry &curves, pxr::VtArray< pxr::GfVec3f > &verts, pxr::VtIntArray &control_point_counts, pxr::VtArray< float > &widths, pxr::TfToken &interpolation, const bool is_cyclic, const bool is_cubic, ReportList *reports)
static void populate_curve_verts_for_bezier(const bke::CurvesGeometry &curves, const Span< float3 > positions, const Span< float3 > handles_l, const Span< float3 > handles_r, pxr::VtArray< pxr::GfVec3f > &verts, pxr::VtIntArray &control_point_counts, pxr::VtArray< int > &segments, const bool is_cyclic)
static void populate_curve_widths(const bke::CurvesGeometry &curves, pxr::VtArray< float > &widths)
std::optional< pxr::SdfValueTypeName > convert_blender_type_to_usd(const eCustomDataType blender_type, bool use_color3f_type)
void copy_blender_buffer_to_primvar(const VArray< BlenderT > &buffer, const pxr::UsdTimeCode timecode, const pxr::UsdGeomPrimvar &primvar, pxr::UsdUtilsSparseValueWriter &value_writer)
bool RNA_enum_name_from_value(const EnumPropertyItem *item, int value, const char **r_name)
const EnumPropertyItem rna_enum_curves_type_items[]
Definition rna_curves.cc:22
signed char int8_t
Definition stdint.h:75
CurvesGeometry geometry