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>
45pxr::UsdGeomBasisCurves USDCurvesWriter::DefineUsdGeomBasisCurves(pxr::VtValue curve_basis,
47 const bool is_cubic)
const
49 pxr::UsdGeomBasisCurves basis_curves = pxr::UsdGeomBasisCurves::Define(
54 basis_curves.CreateTypeAttr(pxr::VtValue(pxr::UsdGeomTokens->cubic));
55 basis_curves.CreateBasisAttr(curve_basis);
58 basis_curves.CreateTypeAttr(pxr::VtValue(pxr::UsdGeomTokens->linear));
62 basis_curves.CreateWrapAttr(pxr::VtValue(pxr::UsdGeomTokens->periodic));
64 else if (curve_basis == pxr::VtValue(pxr::UsdGeomTokens->catmullRom)) {
69 basis_curves.CreateWrapAttr(pxr::VtValue(pxr::UsdGeomTokens->pinned));
72 basis_curves.CreateWrapAttr(pxr::VtValue(pxr::UsdGeomTokens->nonperiodic));
82 widths.resize(radii.
size());
84 widths[
i] = radii[
i] * 2.0f;
89 const pxr::VtArray<int> &segments,
90 const pxr::VtIntArray &control_point_counts,
95 return pxr::TfToken();
98 const size_t accumulated_control_point_count = std::accumulate(
99 control_point_counts.begin(), control_point_counts.end(), 0);
104 if (widths.size() == accumulated_control_point_count) {
105 return pxr::UsdGeomTokens->vertex;
108 size_t expectedVaryingSize = std::accumulate(segments.begin(), segments.end(), 0);
110 expectedVaryingSize += control_point_counts.size();
113 if (widths.size() == expectedVaryingSize) {
114 return pxr::UsdGeomTokens->varying;
118 return pxr::TfToken();
123 pxr::VtArray<pxr::GfVec3f> &
verts,
124 pxr::VtIntArray &control_point_counts,
125 pxr::VtArray<int> &segments,
132 const IndexRange points = points_by_curve[i_curve];
133 for (
const int i_point : points) {
135 pxr::GfVec3f(positions[i_point][0], positions[i_point][1], positions[i_point][2]));
138 const int tot_points = points.
size();
139 control_point_counts[i_curve] = tot_points;
148 segments[i_curve] = tot_points;
151 segments[i_curve] = (tot_points - 4) + 1;
154 segments[i_curve] = tot_points - 1;
160 pxr::VtArray<pxr::GfVec3f> &
verts,
161 pxr::VtIntArray &control_point_counts,
162 pxr::VtArray<float> &widths,
163 pxr::TfToken &interpolation,
171 pxr::VtArray<int> segments(num_curves);
174 curves, positions,
verts, control_point_counts, segments,
is_cyclic, is_cubic);
178 widths, segments, control_point_counts,
is_cyclic, reports);
185 pxr::VtArray<pxr::GfVec3f> &
verts,
186 pxr::VtIntArray &control_point_counts,
187 pxr::VtArray<int> &segments,
190 const int bezier_vstep = 3;
195 const IndexRange points = points_by_curve[i_curve];
196 const int start_point_index = points[0];
197 const int last_point_index = points[points.
size() - 1];
199 const int start_verts_count =
verts.size();
201 for (
int i_point = start_point_index; i_point < last_point_index; i_point++) {
208 pxr::GfVec3f(positions[i_point][0], positions[i_point][1], positions[i_point][2]));
211 verts.push_back(pxr::GfVec3f(right_handle[0], right_handle[1], right_handle[2]));
214 verts.push_back(pxr::GfVec3f(left_handle[0], left_handle[1], left_handle[2]));
217 verts.push_back(pxr::GfVec3f(positions[last_point_index][0],
218 positions[last_point_index][1],
219 positions[last_point_index][2]));
226 verts.push_back(pxr::GfVec3f(right_handle[0], right_handle[1], right_handle[2]));
229 verts.push_back(pxr::GfVec3f(left_handle[0], left_handle[1], left_handle[2]));
232 const int tot_points =
verts.size() - start_verts_count;
233 control_point_counts[i_curve] = tot_points;
236 segments[i_curve] = tot_points / bezier_vstep;
239 segments[i_curve] = ((tot_points - 4) / bezier_vstep) + 1;
245 pxr::VtArray<pxr::GfVec3f> &
verts,
246 pxr::VtIntArray &control_point_counts,
247 pxr::VtArray<float> &widths,
248 pxr::TfToken &interpolation,
258 pxr::VtArray<int> segments(num_curves);
261 curves, positions, *handles_l, *handles_r,
verts, control_point_counts, segments,
is_cyclic);
265 widths, segments, control_point_counts,
is_cyclic, reports);
269 pxr::VtArray<pxr::GfVec3f> &
verts,
270 pxr::VtIntArray &control_point_counts,
271 pxr::VtArray<float> &widths,
272 pxr::VtArray<double> &knots,
273 pxr::VtArray<double> &weights,
274 pxr::VtArray<int> &orders,
275 pxr::TfToken &interpolation,
281 orders.resize(num_curves);
285 const std::optional<Span<float>> nurbs_weights = curves.
nurbs_weights();
294 const IndexRange points = points_by_curve[i_curve];
295 const size_t curr_vert_num =
verts.size();
296 for (
const int i_point : points) {
298 pxr::GfVec3f(positions[i_point][0], positions[i_point][1], positions[i_point][2]));
299 widths.push_back(radii[i_point] * 2.0f);
303 for (
const int i_point : points) {
304 weights.push_back((*nurbs_weights)[i_point]);
310 for (
const int i_point : points.
take_front(geom_orders[i_curve] - 1)) {
312 pxr::GfVec3f(positions[i_point][0], positions[i_point][1], positions[i_point][2]));
313 widths.push_back(radii[i_point] * 2.0f);
315 weights.push_back((*nurbs_weights)[i_point]);
320 const int tot_blender_points = int(points.
size());
321 const int tot_usd_points = int(
verts.size() - curr_vert_num);
322 control_point_counts[i_curve] = tot_usd_points;
324 const int8_t order = geom_orders[i_curve];
325 orders[i_curve] = int(geom_orders[i_curve]);
335 custom_knots_by_curve[i_curve],
341 for (
int i_knot = 0; i_knot < knots_num; i_knot++) {
342 knots.push_back(
double(temp_knots[i_knot]));
347 int zeroth_knot_index = knots.size() - knots_num;
349 knots[zeroth_knot_index] = knots[zeroth_knot_index + 1] -
350 (knots[knots.size() - 2] - knots[knots.size() - 3]);
351 knots[knots.size() - 1] = knots[knots.size() - 2] +
352 (knots[zeroth_knot_index + 2] - knots[zeroth_knot_index + 1]);
355 knots[zeroth_knot_index] = knots[zeroth_knot_index + 1];
356 knots[knots.size() - 1] = knots[knots.size() - 2];
360 interpolation = pxr::UsdGeomTokens->vertex;
363void USDCurvesWriter::set_writer_attributes_for_nurbs(
364 const pxr::UsdGeomNurbsCurves &usd_nurbs_curves,
365 pxr::VtArray<double> &knots,
366 pxr::VtArray<double> &weights,
367 pxr::VtArray<int> &orders,
368 const pxr::UsdTimeCode time)
370 pxr::UsdAttribute attr_knots = usd_nurbs_curves.CreateKnotsAttr(pxr::VtValue(),
true);
372 pxr::UsdAttribute attr_weights = usd_nurbs_curves.CreatePointWeightsAttr(pxr::VtValue(),
true);
374 pxr::UsdAttribute attr_order = usd_nurbs_curves.CreateOrderAttr(pxr::VtValue(),
true);
378void USDCurvesWriter::set_writer_attributes(pxr::UsdGeomCurves &usd_curves,
379 pxr::VtArray<pxr::GfVec3f> &
verts,
380 pxr::VtIntArray &control_point_counts,
381 pxr::VtArray<float> &widths,
382 const pxr::UsdTimeCode time,
383 const pxr::TfToken interpolation)
385 pxr::UsdAttribute attr_points = usd_curves.CreatePointsAttr(pxr::VtValue(),
true);
388 pxr::UsdAttribute attr_vertex_counts = usd_curves.CreateCurveVertexCountsAttr(pxr::VtValue(),
392 if (!widths.empty()) {
393 pxr::UsdAttribute attr_widths = usd_curves.CreateWidthsAttr(pxr::VtValue(),
true);
396 usd_curves.SetWidthsInterpolation(interpolation);
403 switch (blender_domain) {
405 return is_bezier ? pxr::UsdGeomTokens->varying : pxr::UsdGeomTokens->vertex;
407 return pxr::UsdGeomTokens->uniform;
429 set.
add_new(
"handle_type_left");
430 set.
add_new(
"handle_type_right");
443 const pxr::UsdGeomCurves &usd_curves)
452 if (!pv_interp || !pv_type) {
455 "Attribute '%s' (Blender domain %d, type %d) cannot be converted to USD",
468 const pxr::TfToken pv_name(
470 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_curves);
472 pxr::UsdGeomPrimvar pv_attr = pv_api.CreatePrimvar(pv_name, *pv_type, *pv_interp);
477void USDCurvesWriter::write_uv_data(
const bke::AttributeIter &attr,
478 const pxr::UsdGeomCurves &usd_curves)
486 const pxr::TfToken pv_name(
488 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_curves);
490 pxr::UsdGeomPrimvar pv_uv = pv_api.CreatePrimvar(
491 pv_name, pxr::SdfValueTypeNames->TexCoord2fArray, pxr::UsdGeomTokens->uniform);
496void USDCurvesWriter::write_velocities(
const bke::CurvesGeometry &curves,
497 const pxr::UsdGeomCurves &usd_curves)
499 const VArraySpan velocity = *curves.attributes().lookup<
float3>(
"velocity",
501 if (velocity.is_empty()) {
506 Span<pxr::GfVec3f>
data = velocity.cast<pxr::GfVec3f>();
507 pxr::VtVec3fArray usd_velocities;
508 usd_velocities.assign(
data.begin(),
data.end());
511 pxr::UsdAttribute attr_vel = usd_curves.CreateVelocitiesAttr(pxr::VtValue(),
true);
515void USDCurvesWriter::write_custom_data(
const bke::CurvesGeometry &curves,
516 const pxr::UsdGeomCurves &usd_curves)
518 const bke::AttributeAccessor attributes = curves.attributes();
520 attributes.foreach_attribute([&](
const bke::AttributeIter &iter) {
531 this->write_uv_data(iter, usd_curves);
537 this->write_generic_data(curves, iter, usd_curves);
545 std::unique_ptr<
Curves, std::function<void(
Curves *)>> converted_curves;
547 switch (context.object->type) {
549 const Curve *legacy_curve =
static_cast<Curve *
>(context.object->data);
550 converted_curves = std::unique_ptr<
Curves, std::function<void(
Curves *)>>(
552 curves_id = converted_curves.get();
556 curves_id =
static_cast<Curves *
>(context.object->data);
568 const std::array<int, CURVE_TYPES_NUM> &curve_type_counts = curves.
curve_type_counts();
569 const int number_of_curve_types = std::count_if(curve_type_counts.begin(),
570 curve_type_counts.end(),
571 [](
const int count) { return count > 0; });
572 if (number_of_curve_types > 1) {
581 "Cannot export mixed cyclic and non-cyclic curves in the same Curves object");
588 if (first_frame_curve_type == -1) {
589 first_frame_curve_type = curve_type;
591 else if (first_frame_curve_type != curve_type) {
592 const char *first_frame_curve_type_name =
nullptr;
596 const char *current_curve_type_name =
nullptr;
602 "USD does not support animating curve types. The curve type changes from %s to "
604 IFACE_(first_frame_curve_type_name),
605 IFACE_(current_curve_type_name),
611 pxr::VtArray<pxr::GfVec3f>
verts;
612 pxr::VtIntArray control_point_counts;
613 pxr::VtArray<float> widths;
614 pxr::TfToken interpolation;
616 pxr::UsdGeomBasisCurves usd_basis_curves;
617 pxr::UsdGeomNurbsCurves usd_nurbs_curves;
618 pxr::UsdGeomCurves *usd_curves =
nullptr;
620 control_point_counts.resize(curves.
curves_num());
621 switch (curve_type) {
623 usd_basis_curves = DefineUsdGeomBasisCurves(pxr::VtValue(),
is_cyclic,
false);
624 usd_curves = &usd_basis_curves;
630 usd_basis_curves = DefineUsdGeomBasisCurves(
631 pxr::VtValue(pxr::UsdGeomTokens->catmullRom),
is_cyclic,
true);
632 usd_curves = &usd_basis_curves;
638 usd_basis_curves = DefineUsdGeomBasisCurves(
639 pxr::VtValue(pxr::UsdGeomTokens->bezier),
is_cyclic,
true);
640 usd_curves = &usd_basis_curves;
646 pxr::VtArray<double> knots;
647 pxr::VtArray<double> weights;
648 pxr::VtArray<int> orders;
652 usd_curves = &usd_nurbs_curves;
656 control_point_counts,
664 set_writer_attributes_for_nurbs(usd_nurbs_curves, knots, weights, orders, time);
672 this->set_writer_attributes(
673 *usd_curves,
verts, control_point_counts, widths, time, interpolation);
679 this->write_velocities(curves, *usd_curves);
680 this->write_custom_data(curves, *usd_curves);
683 auto prim = usd_curves->GetPrim();
690 const pxr::UsdGeomCurves &usd_curves)
692 if (context.object->totcol == 0) {
696 bool curve_material_bound =
false;
697 for (
int mat_num = 0; mat_num < context.object->totcol; mat_num++) {
699 if (material ==
nullptr) {
703 pxr::UsdPrim curve_prim = usd_curves.GetPrim();
704 pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(curve_prim);
706 api.Bind(usd_material);
707 pxr::UsdShadeMaterialBindingAPI::Apply(curve_prim);
711 usd_curves.CreateDoubleSidedAttr(
714 curve_material_bound =
true;
718 if (!curve_material_bound) {
720 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.
Material * BKE_object_material_get(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()
BMesh const char void * data
constexpr int64_t size() const
constexpr IndexRange take_front(int64_t n) const
bool contains(const Key &key) const
void add_new(const Key &key)
constexpr const char * c_str() const
IndexRange index_range() const
GAttributeReader get() const
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
const std::array< int, CURVE_TYPES_NUM > & curve_type_counts() const
VArray< float > radius() const
std::optional< Span< float > > nurbs_weights() const
Span< float > nurbs_custom_knots() const
VArray< int8_t > nurbs_knots_modes() const
std::optional< Span< float3 > > handle_positions_left() const
Span< float3 > positions() const
std::optional< Span< float3 > > handle_positions_right() const
std::optional< Bounds< float3 > > bounds_min_max(bool use_radius=true) const
OffsetIndices< int > nurbs_custom_knots_by_curve() const
VArray< int8_t > curve_types() const
VArray< bool > cyclic() const
VArray< int8_t > nurbs_orders() const
pxr::UsdShadeMaterial ensure_usd_material(const HierarchyContext &context, Material *material) const
void author_extent(const pxr::UsdGeomBoundable &boundable, const pxr::UsdTimeCode time)
ReportList * reports() const
pxr::UsdTimeCode get_export_time_code() const
pxr::UsdUtilsSparseValueWriter usd_value_writer_
void write_id_properties(const pxr::UsdPrim &prim, const ID &id, pxr::UsdTimeCode=pxr::UsdTimeCode::Default()) const
const USDExporterContext usd_export_context_
void do_write(HierarchyContext &context) override
void assign_materials(const HierarchyContext &context, const pxr::UsdGeomCurves &usd_curves)
static bool is_cyclic(const Nurb *nu)
BooleanMix booleans_mix_calc(const VArray< bool > &varray, IndexRange range_to_check)
int knots_num(int points_num, int8_t order, bool cyclic)
void load_curve_knots(KnotsMode mode, int points_num, int8_t order, bool cyclic, IndexRange curve_knots, Span< float > custom_knots, MutableSpan< float > knots)
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)
void copy_blender_attribute_to_primvar(const GVArray &attribute, const bke::AttrType data_type, const pxr::UsdTimeCode time, const pxr::UsdGeomPrimvar &primvar, pxr::UsdUtilsSparseValueWriter &value_writer)
static bool is_excluded_attr(StringRefNull name)
std::optional< pxr::SdfValueTypeName > convert_blender_type_to_usd(const bke::AttrType blender_type, bool use_color3f_type)
void copy_blender_buffer_to_primvar(const VArray< BlenderT > &buffer, const pxr::UsdTimeCode time, const pxr::UsdGeomPrimvar &primvar, pxr::UsdUtilsSparseValueWriter &value_writer)
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)
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)
std::string make_safe_name(const StringRef name, bool allow_unicode)
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)
void set_attribute(const pxr::UsdAttribute &attr, const USDT value, pxr::UsdTimeCode time, pxr::UsdUtilsSparseValueWriter &value_writer)
static void populate_curve_widths(const bke::CurvesGeometry &curves, pxr::VtArray< float > &widths)
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< double > &weights, pxr::VtArray< int > &orders, pxr::TfToken &interpolation, const bool is_cyclic)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
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