Blender V4.3
usd_reader_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
8#include "BKE_geometry_set.hh"
9#include "BKE_object.hh"
10#include "BKE_pointcloud.hh"
11
12#include "BLI_span.hh"
13
14#include "DNA_object_types.h"
16
17#include <pxr/usd/usdGeom/primvar.h>
18#include <pxr/usd/usdGeom/primvarsAPI.h>
19
20namespace blender::io::usd {
21
22USDPointsReader::USDPointsReader(const pxr::UsdPrim &prim,
23 const USDImportParams &import_params,
24 const ImportSettings &settings)
25 : USDGeomReader(prim, import_params, settings), points_prim_(prim)
26{
27}
28
30{
31 return bool(points_prim_);
32}
33
34void USDPointsReader::create_object(Main *bmain, double /*motionSampleTime*/)
35{
36 PointCloud *point_cloud = static_cast<PointCloud *>(BKE_pointcloud_add(bmain, name_.c_str()));
38 object_->data = point_cloud;
39}
40
41void USDPointsReader::read_object_data(Main *bmain, double motionSampleTime)
42{
43 if (!points_prim_) {
44 /* Invalid prim, so we pass. */
45 return;
46 }
47
48 const USDMeshReadParams params = create_mesh_read_params(motionSampleTime,
50
51 PointCloud *point_cloud = static_cast<PointCloud *>(object_->data);
52
55
56 read_geometry(geometry_set, params, nullptr);
57
58 PointCloud *read_point_cloud =
59 geometry_set.get_component_for_write<bke::PointCloudComponent>().release();
60
61 if (read_point_cloud != point_cloud) {
62 BKE_pointcloud_nomain_to_pointcloud(read_point_cloud, point_cloud);
63 }
64
65 if (is_animated()) {
66 /* If the point cloud has animated positions or attributes, we add the cache
67 * modifier. */
69 }
70
71 /* Update the transform. */
72 USDXformReader::read_object_data(bmain, motionSampleTime);
73}
74
77 const char ** /*r_err_str*/)
78{
79 if (!points_prim_) {
80 /* Invalid prim, so we pass. */
81 return;
82 }
83
84 PointCloud *point_cloud = geometry_set.get_pointcloud_for_write();
85
86 /* Get the existing point cloud data. */
87 pxr::VtVec3fArray usd_positions;
88 points_prim_.GetPointsAttr().Get(&usd_positions, params.motion_sample_time);
89
90 if (point_cloud->totpoint != usd_positions.size()) {
91 /* Size changed so we must reallocate. */
92 point_cloud = BKE_pointcloud_new_nomain(usd_positions.size());
93 }
94
95 /* Update point positions and radii */
96 static_assert(sizeof(pxr::GfVec3f) == sizeof(float3));
97 MutableSpan<float3> positions = point_cloud->positions_for_write();
98 positions.copy_from(Span(usd_positions.data(), usd_positions.size()).cast<float3>());
99
100 pxr::VtFloatArray usd_widths;
101 points_prim_.GetWidthsAttr().Get(&usd_widths, params.motion_sample_time);
102
103 if (!usd_widths.empty()) {
104 bke::MutableAttributeAccessor attributes = point_cloud->attributes_for_write();
105 bke::SpanAttributeWriter<float> radii = attributes.lookup_or_add_for_write_only_span<float>(
106 "radius", bke::AttrDomain::Point);
107
108 const pxr::TfToken widths_interp = points_prim_.GetWidthsInterpolation();
109 if (widths_interp == pxr::UsdGeomTokens->constant) {
110 radii.span.fill(usd_widths[0] / 2.0f);
111 }
112 else {
113 for (int i_point = 0; i_point < usd_widths.size(); i_point++) {
114 radii.span[i_point] = usd_widths[i_point] / 2.0f;
115 }
116 }
117
118 radii.finish();
119 }
120
121 /* TODO: Read in ID and normal data.
122 * See UsdGeomPoints::GetIdsAttr and UsdGeomPointBased::GetNormalsAttr */
123
124 /* Read in velocity and generic data. */
125 read_velocities(point_cloud, params.motion_sample_time);
126 read_custom_data(point_cloud, params.motion_sample_time);
127
128 geometry_set.replace_pointcloud(point_cloud);
129}
130
131void USDPointsReader::read_velocities(PointCloud *point_cloud, const double motionSampleTime) const
132{
133 pxr::VtVec3fArray velocities;
134 points_prim_.GetVelocitiesAttr().Get(&velocities, motionSampleTime);
135
136 if (!velocities.empty()) {
137 bke::MutableAttributeAccessor attributes = point_cloud->attributes_for_write();
139 attributes.lookup_or_add_for_write_only_span<float3>("velocity", bke::AttrDomain::Point);
140
141 Span<pxr::GfVec3f> usd_data(velocities.data(), velocities.size());
142 velocity.span.copy_from(usd_data.cast<float3>());
143 velocity.finish();
144 }
145}
146
148 const double motionSampleTime) const
149{
150 pxr::UsdGeomPrimvarsAPI pv_api(points_prim_);
151
152 std::vector<pxr::UsdGeomPrimvar> primvars = pv_api.GetPrimvarsWithValues();
153 for (const pxr::UsdGeomPrimvar &pv : primvars) {
154 if (!pv.HasValue()) {
155 continue;
156 }
157
158 const pxr::SdfValueTypeName pv_type = pv.GetTypeName();
159
161 const std::optional<eCustomDataType> type = convert_usd_type_to_blender(pv_type);
162
163 if (!type.has_value()) {
164 return;
165 }
166
167 bke::MutableAttributeAccessor attributes = point_cloud->attributes_for_write();
168 copy_primvar_to_blender_attribute(pv, motionSampleTime, *type, domain, {}, attributes);
169 }
170}
171
173{
174 if (!points_prim_) {
175 return false;
176 }
177
178 bool is_animated = false;
179
180 is_animated |= points_prim_.GetPointsAttr().ValueMightBeTimeVarying();
181
182 is_animated |= points_prim_.GetVelocitiesAttr().ValueMightBeTimeVarying();
183
184 is_animated |= points_prim_.GetWidthsAttr().ValueMightBeTimeVarying();
185
186 pxr::UsdGeomPrimvarsAPI pv_api(points_prim_);
187 std::vector<pxr::UsdGeomPrimvar> primvars = pv_api.GetPrimvarsWithValues();
188 for (const pxr::UsdGeomPrimvar &pv : primvars) {
189 is_animated |= pv.ValueMightBeTimeVarying();
190 }
191
192 return is_animated;
193}
194
195} // namespace blender::io::usd
General operations, lookup, etc. for blender objects.
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
General operations for point clouds.
void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src, PointCloud *pointcloud_dst)
void * BKE_pointcloud_add(Main *bmain, const char *name)
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
Object is a sort of wrapper for general info.
@ OB_POINTCLOUD
void read_object_data(Main *bmain, double motionSampleTime) override
void create_object(Main *bmain, double motionSampleTime) override
void read_geometry(bke::GeometrySet &geometry_set, USDMeshReadParams params, const char **r_err_str) override
void read_custom_data(PointCloud *point_cloud, const double motionSampleTime) const
USDPointsReader(const pxr::UsdPrim &prim, const USDImportParams &import_params, const ImportSettings &settings)
void read_velocities(PointCloud *point_cloud, const double motionSampleTime) const
const USDImportParams & import_params_
void read_object_data(Main *bmain, double motionSampleTime) override
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
USDMeshReadParams create_mesh_read_params(const double motion_sample_time, const int read_flags)
std::optional< eCustomDataType > convert_usd_type_to_blender(const pxr::SdfValueTypeName usd_type)
void copy_primvar_to_blender_attribute(const pxr::UsdGeomPrimvar &primvar, const pxr::UsdTimeCode timecode, const eCustomDataType data_type, const bke::AttrDomain domain, const OffsetIndices< int > face_indices, bke::MutableAttributeAccessor attributes)
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
PointCloud * get_pointcloud_for_write()
void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
static GeometrySet from_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)