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>
41pxr::UsdGeomBasisCurves USDCurvesWriter::DefineUsdGeomBasisCurves(pxr::VtValue curve_basis,
43 const bool is_cubic)
const
45 pxr::UsdGeomBasisCurves basis_curves = pxr::UsdGeomBasisCurves::Define(
50 basis_curves.CreateTypeAttr(pxr::VtValue(pxr::UsdGeomTokens->cubic));
51 basis_curves.CreateBasisAttr(curve_basis);
54 basis_curves.CreateTypeAttr(pxr::VtValue(pxr::UsdGeomTokens->linear));
58 basis_curves.CreateWrapAttr(pxr::VtValue(pxr::UsdGeomTokens->periodic));
60 else if (curve_basis == pxr::VtValue(pxr::UsdGeomTokens->catmullRom)) {
65 basis_curves.CreateWrapAttr(pxr::VtValue(pxr::UsdGeomTokens->pinned));
68 basis_curves.CreateWrapAttr(pxr::VtValue(pxr::UsdGeomTokens->nonperiodic));
80 widths.resize(radii.varray.size());
82 for (
const int i : radii.varray.index_range()) {
83 widths[i] = radii.
varray[i] * 2.0f;
88 const pxr::VtArray<int> &segments,
89 const pxr::VtIntArray &control_point_counts,
94 return pxr::TfToken();
97 const size_t accumulated_control_point_count = std::accumulate(
98 control_point_counts.begin(), control_point_counts.end(), 0);
103 if (widths.size() == accumulated_control_point_count) {
104 return pxr::UsdGeomTokens->vertex;
107 size_t expectedVaryingSize = std::accumulate(segments.begin(), segments.end(), 0);
109 expectedVaryingSize += control_point_counts.size();
112 if (widths.size() == expectedVaryingSize) {
113 return pxr::UsdGeomTokens->varying;
117 return pxr::TfToken();
122 pxr::VtArray<pxr::GfVec3f> &
verts,
123 pxr::VtIntArray &control_point_counts,
124 pxr::VtArray<int> &segments,
128 const OffsetIndices points_by_curve = curves.points_by_curve();
129 for (
const int i_curve : curves.curves_range()) {
131 const IndexRange points = points_by_curve[i_curve];
132 for (
const int i_point : points) {
134 pxr::GfVec3f(positions[i_point][0], positions[i_point][1], positions[i_point][2]));
137 const int tot_points = points.
size();
138 control_point_counts[i_curve] = tot_points;
147 segments[i_curve] = tot_points;
150 segments[i_curve] = (tot_points - 4) + 1;
153 segments[i_curve] = tot_points - 1;
159 pxr::VtArray<pxr::GfVec3f> &
verts,
160 pxr::VtIntArray &control_point_counts,
161 pxr::VtArray<float> &widths,
162 pxr::TfToken &interpolation,
167 const int num_curves = curves.curve_num;
170 pxr::VtArray<int> segments(num_curves);
173 curves, positions,
verts, control_point_counts, segments,
is_cyclic, is_cubic);
177 widths, segments, control_point_counts,
is_cyclic, reports);
184 pxr::VtArray<pxr::GfVec3f> &
verts,
185 pxr::VtIntArray &control_point_counts,
186 pxr::VtArray<int> &segments,
189 const int bezier_vstep = 3;
190 const OffsetIndices points_by_curve = curves.points_by_curve();
192 for (
const int i_curve : curves.curves_range()) {
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];
198 const int start_verts_count =
verts.
size();
200 for (
int i_point = start_point_index; i_point < last_point_index; i_point++) {
207 pxr::GfVec3f(positions[i_point][0], positions[i_point][1], positions[i_point][2]));
210 verts.push_back(pxr::GfVec3f(right_handle[0], right_handle[1], right_handle[2]));
213 verts.push_back(pxr::GfVec3f(left_handle[0], left_handle[1], left_handle[2]));
216 verts.push_back(pxr::GfVec3f(positions[last_point_index][0],
217 positions[last_point_index][1],
218 positions[last_point_index][2]));
225 verts.push_back(pxr::GfVec3f(right_handle[0], right_handle[1], right_handle[2]));
228 verts.push_back(pxr::GfVec3f(left_handle[0], left_handle[1], left_handle[2]));
231 const int tot_points =
verts.
size() - start_verts_count;
232 control_point_counts[i_curve] = tot_points;
235 segments[i_curve] = tot_points / bezier_vstep;
238 segments[i_curve] = ((tot_points - 4) / bezier_vstep) + 1;
244 pxr::VtArray<pxr::GfVec3f> &
verts,
245 pxr::VtIntArray &control_point_counts,
246 pxr::VtArray<float> &widths,
247 pxr::TfToken &interpolation,
251 const int num_curves = curves.curve_num;
254 const Span<float3> handles_l = curves.handle_positions_left();
255 const Span<float3> handles_r = curves.handle_positions_right();
257 pxr::VtArray<int> segments(num_curves);
260 curves, positions, handles_l, handles_r,
verts, control_point_counts, segments,
is_cyclic);
264 widths, segments, control_point_counts,
is_cyclic, reports);
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,
278 const int num_curves = curves.curve_num;
279 orders.resize(num_curves);
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) {
291 pxr::GfVec3f(positions[i_point][0], positions[i_point][1], positions[i_point][2]));
294 const int tot_points = points.
size();
295 control_point_counts[i_curve] = tot_points;
297 const int8_t order = geom_orders[i_curve];
298 orders[i_curve] =
int(geom_orders[i_curve]);
308 for (
int i_knot = 0; i_knot < knots_num; i_knot++) {
309 knots.push_back(
double(temp_knots[i_knot]));
314 int zeroth_knot_index = knots.size() - knots_num;
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]);
322 knots[zeroth_knot_index] = knots[zeroth_knot_index + 1];
323 knots[knots.size() - 1] = knots[knots.size() - 2];
328 interpolation = pxr::UsdGeomTokens->vertex;
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)
337 pxr::UsdAttribute attr_knots = usd_nurbs_curves.CreateKnotsAttr(pxr::VtValue(),
true);
339 pxr::UsdAttribute attr_order = usd_nurbs_curves.CreateOrderAttr(pxr::VtValue(),
true);
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)
350 pxr::UsdAttribute attr_points = usd_curves.CreatePointsAttr(pxr::VtValue(),
true);
353 pxr::UsdAttribute attr_vertex_counts = usd_curves.CreateCurveVertexCountsAttr(pxr::VtValue(),
355 usd_value_writer_.SetAttribute(attr_vertex_counts, pxr::VtValue(control_point_counts), timecode);
357 if (!widths.empty()) {
358 pxr::UsdAttribute attr_widths = usd_curves.CreateWidthsAttr(pxr::VtValue(),
true);
361 usd_curves.SetWidthsInterpolation(interpolation);
368 switch (blender_domain) {
370 return is_bezier ? pxr::UsdGeomTokens->varying : pxr::UsdGeomTokens->vertex;
372 return pxr::UsdGeomTokens->uniform;
381 const pxr::UsdGeomCurves &usd_curves)
390 if (!pv_interp || !pv_type) {
393 "Attribute '%s' (Blender domain %d, type %d) cannot be converted to USD",
401 if (attribute.is_empty()) {
406 const pxr::TfToken pv_name(
408 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_curves);
410 pxr::UsdGeomPrimvar pv_attr = pv_api.CreatePrimvar(pv_name, *pv_type, *pv_interp);
416void USDCurvesWriter::write_uv_data(
const bke::AttributeIter &attr,
417 const pxr::UsdGeomCurves &usd_curves)
420 if (buffer.is_empty()) {
425 const pxr::TfToken pv_name(
427 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_curves);
429 pxr::UsdGeomPrimvar pv_uv = pv_api.CreatePrimvar(
430 pv_name, pxr::SdfValueTypeNames->TexCoord2fArray, pxr::UsdGeomTokens->uniform);
435void USDCurvesWriter::write_custom_data(
const bke::CurvesGeometry &curves,
436 const pxr::UsdGeomCurves &usd_curves)
438 const bke::AttributeAccessor attributes = curves.attributes();
440 attributes.foreach_attribute([&](
const bke::AttributeIter &iter) {
452 "handle_type_right"))
459 if (usd_export_context_.export_params.export_uvmaps) {
460 this->write_uv_data(iter, usd_curves);
466 this->write_generic_data(curves, iter, usd_curves);
474 std::unique_ptr<
Curves, std::function<void(
Curves *)>> converted_curves;
476 switch (context.object->type) {
478 const Curve *legacy_curve =
static_cast<Curve *
>(context.object->data);
479 converted_curves = std::unique_ptr<
Curves, std::function<void(
Curves *)>>(
481 curves_id = converted_curves.get();
485 curves_id =
static_cast<Curves *
>(context.object->data);
493 if (curves.points_num() == 0) {
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");
510 "Cannot export mixed cyclic and non-cyclic curves in the same Curves object");
514 const pxr::UsdTimeCode timecode = get_export_time_code();
515 const int8_t curve_type = curves.curve_types()[0];
517 if (first_frame_curve_type == -1) {
518 first_frame_curve_type = curve_type;
520 else if (first_frame_curve_type != curve_type) {
521 const char *first_frame_curve_type_name =
nullptr;
525 const char *current_curve_type_name =
nullptr;
531 "USD does not support animating curve types. The curve type changes from %s to "
533 IFACE_(first_frame_curve_type_name),
534 IFACE_(current_curve_type_name),
535 timecode.GetValue());
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;
545 pxr::UsdGeomBasisCurves usd_basis_curves;
546 pxr::UsdGeomNurbsCurves usd_nurbs_curves;
547 pxr::UsdGeomCurves *usd_curves =
nullptr;
549 control_point_counts.resize(curves.curves_num());
550 switch (curve_type) {
552 usd_basis_curves = DefineUsdGeomBasisCurves(pxr::VtValue(),
is_cyclic,
false);
553 usd_curves = &usd_basis_curves;
556 curves,
verts, control_point_counts, widths, interpolation,
is_cyclic,
false, reports());
559 usd_basis_curves = DefineUsdGeomBasisCurves(
560 pxr::VtValue(pxr::UsdGeomTokens->catmullRom),
is_cyclic,
true);
561 usd_curves = &usd_basis_curves;
564 curves,
verts, control_point_counts, widths, interpolation,
is_cyclic,
true, reports());
567 usd_basis_curves = DefineUsdGeomBasisCurves(
568 pxr::VtValue(pxr::UsdGeomTokens->bezier),
is_cyclic,
true);
569 usd_curves = &usd_basis_curves;
572 curves,
verts, control_point_counts, widths, interpolation,
is_cyclic, reports());
575 pxr::VtArray<double> knots;
576 pxr::VtArray<int> orders;
577 orders.resize(curves.curves_num());
579 usd_nurbs_curves = pxr::UsdGeomNurbsCurves::Define(usd_export_context_.stage,
580 usd_export_context_.usd_path);
581 usd_curves = &usd_nurbs_curves;
584 curves,
verts, control_point_counts, widths, knots, orders, interpolation,
is_cyclic);
586 set_writer_attributes_for_nurbs(usd_nurbs_curves, knots, orders, timecode);
594 set_writer_attributes(*usd_curves,
verts, control_point_counts, widths, timecode, interpolation);
596 assign_materials(context, *usd_curves);
598 write_custom_data(curves, *usd_curves);
600 auto prim = usd_curves->GetPrim();
601 write_id_properties(prim, curves_id->
id, timecode);
605 const pxr::UsdGeomCurves &usd_curves)
607 if (context.object->totcol == 0) {
611 bool curve_material_bound =
false;
612 for (
short mat_num = 0; mat_num < context.object->totcol; mat_num++) {
614 if (material ==
nullptr) {
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);
626 usd_curves.CreateDoubleSidedAttr(
629 curve_material_bound =
true;
633 if (!curve_material_bound) {
635 usd_curves.CreateDoubleSidedAttr(pxr::VtValue(
true));
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)
#define BLI_assert_unreachable()
constexpr int64_t size() const
constexpr const char * c_str() const
GAttributeReader lookup(const StringRef attribute_id) const
eCustomDataType data_type
GAttributeReader get() const
ReportList * reports() 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
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)
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[]
const USDExportParams & export_params
const pxr::SdfPath usd_path
const pxr::UsdStageRefPtr stage