Blender V4.5
usd_writer_points.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
7#include "usd_utils.hh"
8
10#include "BKE_attribute.hh"
11#include "BKE_report.hh"
12
14
15#include <pxr/base/vt/array.h>
16#include <pxr/usd/usdGeom/points.h>
17#include <pxr/usd/usdGeom/primvarsAPI.h>
18
19namespace blender::io::usd {
20
22{
23 const pxr::UsdStageRefPtr stage = usd_export_context_.stage;
24 const pxr::SdfPath &usd_path = usd_export_context_.usd_path;
25 const pxr::UsdTimeCode timecode = get_export_time_code();
26
27 const PointCloud *points = static_cast<const PointCloud *>(context.object->data);
28 Span<pxr::GfVec3f> positions = points->positions().cast<pxr::GfVec3f>();
29 VArray<float> radii = points->radius();
30
31 const pxr::UsdGeomPoints usd_points = pxr::UsdGeomPoints::Define(stage, usd_path);
32
33 pxr::VtArray<pxr::GfVec3f> usd_positions;
34 usd_positions.assign(positions.begin(), positions.end());
35
36 pxr::UsdAttribute attr_positions = usd_points.CreatePointsAttr(pxr::VtValue(), true);
37 if (!attr_positions.HasValue()) {
38 attr_positions.Set(usd_positions, pxr::UsdTimeCode::Default());
39 }
40 usd_value_writer_.SetAttribute(attr_positions, usd_positions, timecode);
41
42 if (!radii.is_empty()) {
43 pxr::VtArray<float> usd_widths;
44 usd_widths.resize(radii.size());
45 for (const int i : radii.index_range()) {
46 usd_widths[i] = radii[i] * 2.0f;
47 }
48
49 pxr::UsdAttribute attr_widths = usd_points.CreateWidthsAttr(pxr::VtValue(), true);
50 if (!attr_widths.HasValue()) {
51 attr_widths.Set(usd_widths, pxr::UsdTimeCode::Default());
52 }
53 usd_value_writer_.SetAttribute(attr_widths, usd_widths, timecode);
54 }
55
56 this->write_velocities(points, usd_points, timecode);
57 this->write_custom_data(points, usd_points, timecode);
58
59 this->author_extent(usd_points, points->bounds_min_max(), timecode);
60}
61
62static std::optional<pxr::TfToken> convert_blender_domain_to_usd(
63 const bke::AttrDomain blender_domain)
64{
65 switch (blender_domain) {
67 return pxr::UsdGeomTokens->varying;
68
69 default:
70 return std::nullopt;
71 }
72}
73
74void USDPointsWriter::write_generic_data(const bke::AttributeIter &attr,
75 const pxr::UsdGeomPoints &usd_points,
76 const pxr::UsdTimeCode timecode)
77{
78 const std::optional<pxr::TfToken> pv_interp = convert_blender_domain_to_usd(attr.domain);
79 const std::optional<pxr::SdfValueTypeName> pv_type = convert_blender_type_to_usd(attr.data_type);
80
81 if (!pv_interp || !pv_type) {
82 BKE_reportf(this->reports(),
84 "Attribute '%s' (Blender domain %d, type %d) cannot be converted to USD",
85 attr.name.c_str(),
86 int(attr.domain),
87 attr.data_type);
88 return;
89 }
90
91 const GVArray attribute = *attr.get();
92 if (attribute.is_empty()) {
93 return;
94 }
95
96 const pxr::TfToken pv_name(
98 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_points);
99
100 pxr::UsdGeomPrimvar pv_attr = pv_api.CreatePrimvar(pv_name, *pv_type, *pv_interp);
101
103 attribute, attr.data_type, timecode, pv_attr, usd_value_writer_);
104}
105
106void USDPointsWriter::write_custom_data(const PointCloud *points,
107 const pxr::UsdGeomPoints &usd_points,
108 const pxr::UsdTimeCode timecode)
109{
110 const bke::AttributeAccessor attributes = points->attributes();
111
112 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
113 /* Skip "internal" Blender properties and attributes dealt with elsewhere. */
114 if (iter.name[0] == '.' || bke::attribute_name_is_anonymous(iter.name) ||
115 ELEM(iter.name, "position", "radius", "id", "velocity"))
116 {
117 return;
118 }
119
120 this->write_generic_data(iter, usd_points, timecode);
121 });
122}
123
124void USDPointsWriter::write_velocities(const PointCloud *points,
125 const pxr::UsdGeomPoints &usd_points,
126 const pxr::UsdTimeCode timecode)
127{
128 const VArraySpan velocity = *points->attributes().lookup<float3>(
130 if (velocity.is_empty()) {
131 return;
132 }
133
134 Span<pxr::GfVec3f> data = velocity.cast<pxr::GfVec3f>();
135 pxr::VtArray<pxr::GfVec3f> usd_velocities;
136 usd_velocities.assign(data.begin(), data.end());
137
138 pxr::UsdAttribute attr_vel = usd_points.CreateVelocitiesAttr(pxr::VtValue(), true);
139 if (!attr_vel.HasValue()) {
140 attr_vel.Set(usd_velocities, pxr::UsdTimeCode::Default());
141 }
142
143 usd_value_writer_.SetAttribute(attr_vel, usd_velocities, timecode);
144}
145
146} // namespace blender::io::usd
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
#define ELEM(...)
struct PointCloud PointCloud
BMesh const char void * data
AttributeSet attributes
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr const T * begin() const
Definition BLI_span.hh:220
constexpr const char * c_str() const
GAttributeReader get() const
const pxr::SdfPath & usd_path() const
void author_extent(const pxr::UsdGeomBoundable &boundable, const pxr::UsdTimeCode timecode)
pxr::UsdTimeCode get_export_time_code() const
pxr::UsdUtilsSparseValueWriter usd_value_writer_
const USDExporterContext usd_export_context_
void do_write(HierarchyContext &context) override
bool attribute_name_is_anonymous(const StringRef name)
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)
std::string make_safe_name(const StringRef name, bool allow_unicode)
Definition usd_utils.cc:18
std::optional< pxr::SdfValueTypeName > convert_blender_type_to_usd(const eCustomDataType blender_type, bool use_color3f_type)
VecBase< float, 3 > float3
i
Definition text_draw.cc:230