Blender V4.3
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
9#include "BKE_attribute.hh"
10#include "BKE_report.hh"
11
13
14#include <pxr/base/vt/array.h>
15#include <pxr/usd/usdGeom/points.h>
16#include <pxr/usd/usdGeom/primvarsAPI.h>
17
18namespace blender::io::usd {
19
21{
22 const pxr::UsdStageRefPtr stage = usd_export_context_.stage;
23 const pxr::SdfPath &usd_path = usd_export_context_.usd_path;
24 const pxr::UsdTimeCode timecode = get_export_time_code();
25
26 const PointCloud *points = static_cast<const PointCloud *>(context.object->data);
27 Span<pxr::GfVec3f> positions = points->positions().cast<pxr::GfVec3f>();
28 VArray<float> radii = *points->attributes().lookup<float>("radius", bke::AttrDomain::Point);
29
30 const pxr::UsdGeomPoints usd_points = pxr::UsdGeomPoints::Define(stage, usd_path);
31
32 pxr::VtArray<pxr::GfVec3f> usd_positions;
33 usd_positions.assign(positions.begin(), positions.end());
34
35 pxr::UsdAttribute attr_positions = usd_points.CreatePointsAttr(pxr::VtValue(), true);
36 if (!attr_positions.HasValue()) {
37 attr_positions.Set(usd_positions, pxr::UsdTimeCode::Default());
38 }
39 usd_value_writer_.SetAttribute(attr_positions, usd_positions, timecode);
40
41 if (!radii.is_empty()) {
42 pxr::VtArray<float> usd_widths;
43 usd_widths.resize(radii.size());
44 for (const int i : radii.index_range()) {
45 usd_widths[i] = radii[i] * 2.0f;
46 }
47
48 pxr::UsdAttribute attr_widths = usd_points.CreateWidthsAttr(pxr::VtValue(), true);
49 if (!attr_widths.HasValue()) {
50 attr_widths.Set(usd_widths, pxr::UsdTimeCode::Default());
51 }
52 usd_value_writer_.SetAttribute(attr_widths, usd_widths, timecode);
53 }
54
55 this->write_velocities(points, usd_points, timecode);
56 this->write_custom_data(points, usd_points, timecode);
57
58 const pxr::UsdPrim usd_prim = usd_points.GetPrim();
59 this->set_extents(usd_prim, 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
146void USDPointsWriter::set_extents(const pxr::UsdPrim &prim, const pxr::UsdTimeCode timecode)
147{
148 pxr::UsdGeomBoundable boundable(prim);
149
150 pxr::VtArray<pxr::GfVec3f> extent;
151 pxr::UsdGeomBoundable::ComputeExtentFromPlugins(boundable, timecode, &extent);
152
153 pxr::UsdAttribute attr_extent = boundable.CreateExtentAttr(pxr::VtValue(), true);
154 if (!attr_extent.HasValue()) {
155 attr_extent.Set(extent, pxr::UsdTimeCode::Default());
156 }
157
158 usd_value_writer_.SetAttribute(attr_extent, extent, timecode);
159}
160
161} // namespace blender::io::usd
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
#define ELEM(...)
struct PointCloud PointCloud
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
AttributeSet attributes
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr const T * begin() const
Definition BLI_span.hh:221
constexpr const char * c_str() const
GAttributeReader get() const
const pxr::SdfPath & usd_path() const
pxr::UsdTimeCode get_export_time_code() const
pxr::UsdUtilsSparseValueWriter usd_value_writer_
const USDExporterContext usd_export_context_
virtual void do_write(HierarchyContext &context) override
bool attribute_name_is_anonymous(const StringRef name)
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)
std::optional< pxr::SdfValueTypeName > convert_blender_type_to_usd(const eCustomDataType blender_type, bool use_color3f_type)
VecBase< float, 3 > float3