Blender V4.3
usd_reader_shape.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Nvidia. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_attribute.hh"
6#include "BKE_geometry_set.hh"
7#include "BKE_lib_id.hh"
8#include "BKE_mesh.hh"
9#include "BKE_object.hh"
10#include "BKE_report.hh"
11
12#include "DNA_modifier_types.h"
13#include "DNA_object_types.h"
15
17#include "usd_mesh_utils.hh"
18#include "usd_reader_shape.hh"
19
20#include <pxr/usd/usdGeom/capsule.h>
21#include <pxr/usd/usdGeom/cone.h>
22#include <pxr/usd/usdGeom/cube.h>
23#include <pxr/usd/usdGeom/cylinder.h>
24#include <pxr/usd/usdGeom/sphere.h>
25#include <pxr/usdImaging/usdImaging/capsuleAdapter.h>
26#include <pxr/usdImaging/usdImaging/coneAdapter.h>
27#include <pxr/usdImaging/usdImaging/cubeAdapter.h>
28#include <pxr/usdImaging/usdImaging/cylinderAdapter.h>
29#include <pxr/usdImaging/usdImaging/sphereAdapter.h>
30
31namespace blender::io::usd {
32
33USDShapeReader::USDShapeReader(const pxr::UsdPrim &prim,
34 const USDImportParams &import_params,
35 const ImportSettings &settings)
36 : USDGeomReader(prim, import_params, settings)
37{
38}
39
40void USDShapeReader::create_object(Main *bmain, double /*motionSampleTime*/)
41{
42 Mesh *mesh = BKE_mesh_add(bmain, name_.c_str());
44 object_->data = mesh;
45}
46
47void USDShapeReader::read_object_data(Main *bmain, double motionSampleTime)
48{
49 const USDMeshReadParams params = create_mesh_read_params(motionSampleTime,
51 Mesh *mesh = (Mesh *)object_->data;
52 Mesh *read_mesh = this->read_mesh(mesh, params, nullptr);
53
54 if (read_mesh != mesh) {
55 BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_);
56 if (is_time_varying()) {
58 }
59 }
60
61 USDXformReader::read_object_data(bmain, motionSampleTime);
62}
63
64template<typename Adapter>
65void USDShapeReader::read_values(const double motionSampleTime,
66 pxr::VtVec3fArray &positions,
67 pxr::VtIntArray &face_indices,
68 pxr::VtIntArray &face_counts) const
69{
70 Adapter adapter;
71 pxr::VtValue points_val = adapter.GetPoints(prim_, motionSampleTime);
72
73 if (points_val.IsHolding<pxr::VtVec3fArray>()) {
74 positions = points_val.Get<pxr::VtVec3fArray>();
75 }
76
77 pxr::VtValue topology_val = adapter.GetTopology(prim_, pxr::SdfPath(), motionSampleTime);
78
79 if (topology_val.IsHolding<pxr::HdMeshTopology>()) {
80 const pxr::HdMeshTopology &topology = topology_val.Get<pxr::HdMeshTopology>();
81 face_counts = topology.GetFaceVertexCounts();
82 face_indices = topology.GetFaceVertexIndices();
83 }
84}
85
86bool USDShapeReader::read_mesh_values(double motionSampleTime,
87 pxr::VtVec3fArray &positions,
88 pxr::VtIntArray &face_indices,
89 pxr::VtIntArray &face_counts) const
90{
91 if (prim_.IsA<pxr::UsdGeomCapsule>()) {
92 read_values<pxr::UsdImagingCapsuleAdapter>(
93 motionSampleTime, positions, face_indices, face_counts);
94 return true;
95 }
96
97 if (prim_.IsA<pxr::UsdGeomCylinder>()) {
98 read_values<pxr::UsdImagingCylinderAdapter>(
99 motionSampleTime, positions, face_indices, face_counts);
100 return true;
101 }
102
103 if (prim_.IsA<pxr::UsdGeomCone>()) {
104 read_values<pxr::UsdImagingConeAdapter>(
105 motionSampleTime, positions, face_indices, face_counts);
106 return true;
107 }
108
109 if (prim_.IsA<pxr::UsdGeomCube>()) {
110 read_values<pxr::UsdImagingCubeAdapter>(
111 motionSampleTime, positions, face_indices, face_counts);
112 return true;
113 }
114
115 if (prim_.IsA<pxr::UsdGeomSphere>()) {
116 read_values<pxr::UsdImagingSphereAdapter>(
117 motionSampleTime, positions, face_indices, face_counts);
118 return true;
119 }
120
122 RPT_ERROR,
123 "Unhandled Gprim type: %s (%s)",
124 prim_.GetTypeName().GetText(),
125 prim_.GetPath().GetText());
126 return false;
127}
128
129Mesh *USDShapeReader::read_mesh(Mesh *existing_mesh,
130 const USDMeshReadParams params,
131 const char ** /*r_err_str*/)
132{
133 pxr::VtIntArray face_indices;
134 pxr::VtIntArray face_counts;
135
136 if (!prim_) {
137 return existing_mesh;
138 }
139
140 /* Should have a good set of data by this point-- copy over. */
141 Mesh *active_mesh = mesh_from_prim(existing_mesh, params, face_indices, face_counts);
142
143 if (active_mesh == existing_mesh) {
144 return existing_mesh;
145 }
146
147 MutableSpan<int> face_offsets = active_mesh->face_offsets_for_write();
148 for (const int i : IndexRange(active_mesh->faces_num)) {
149 face_offsets[i] = face_counts[i];
150 }
152
153 /* Don't smooth-shade cubes; we're not worrying about sharpness for Gprims. */
154 bke::mesh_smooth_set(*active_mesh, !prim_.IsA<pxr::UsdGeomCube>());
155
156 MutableSpan<int> corner_verts = active_mesh->corner_verts_for_write();
157 for (const int i : corner_verts.index_range()) {
158 corner_verts[i] = face_indices[i];
159 }
160
161 bke::mesh_calc_edges(*active_mesh, false, false);
162 return active_mesh;
163}
164
167 const char **r_err_str)
168{
169 Mesh *existing_mesh = geometry_set.get_mesh_for_write();
170 Mesh *new_mesh = read_mesh(existing_mesh, params, r_err_str);
171
172 if (new_mesh != existing_mesh) {
173 geometry_set.replace_mesh(new_mesh);
174 }
175}
176
177void USDShapeReader::apply_primvars_to_mesh(Mesh *mesh, const double motionSampleTime) const
178{
179 /* TODO: also handle the displayOpacity primvar. */
180 if (!mesh || !prim_) {
181 return;
182 }
183
184 pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(prim_);
185 std::vector<pxr::UsdGeomPrimvar> primvars = pv_api.GetPrimvarsWithValues();
186
187 pxr::TfToken active_color_name;
188
189 for (const pxr::UsdGeomPrimvar &pv : primvars) {
190 if (!pv.HasValue()) {
193 "Skipping primvar %s, mesh %s -- no value",
194 pv.GetName().GetText(),
195 &mesh->id.name[2]);
196 continue;
197 }
198
199 if (!pv.GetAttr().GetTypeName().IsArray()) {
200 /* Non-array attributes are technically improper USD. */
201 continue;
202 }
203
204 const pxr::TfToken name = pxr::UsdGeomPrimvar::StripPrimvarsName(pv.GetPrimvarName());
205
206 /* Skip reading primvars that have been read before and are not time varying. */
207 if (primvar_time_varying_map_.contains(name) && !primvar_time_varying_map_.lookup(name)) {
208 continue;
209 }
210
211 const pxr::SdfValueTypeName sdf_type = pv.GetTypeName();
212
213 const std::optional<eCustomDataType> type = convert_usd_type_to_blender(sdf_type);
214 if (type == CD_PROP_COLOR) {
215 /* Set the active color name to 'displayColor', if a color primvar
216 * with this name exists. Otherwise, use the name of the first
217 * color primvar we find for the active color. */
218 if (active_color_name.IsEmpty() || name == usdtokens::displayColor) {
219 active_color_name = name;
220 }
221 }
222
223 read_generic_mesh_primvar(mesh, pv, motionSampleTime, false);
224
225 /* Record whether the primvar attribute might be time varying. */
226 if (!primvar_time_varying_map_.contains(name)) {
227 primvar_time_varying_map_.add(name, pv.ValueMightBeTimeVarying());
228 }
229 }
230
231 if (!active_color_name.IsEmpty()) {
232 BKE_id_attributes_default_color_set(&mesh->id, active_color_name.GetText());
233 BKE_id_attributes_active_color_set(&mesh->id, active_color_name.GetText());
234 }
235}
236
237Mesh *USDShapeReader::mesh_from_prim(Mesh *existing_mesh,
238 const USDMeshReadParams params,
239 pxr::VtIntArray &face_indices,
240 pxr::VtIntArray &face_counts) const
241{
242 pxr::VtVec3fArray positions;
243
244 if (!read_mesh_values(params.motion_sample_time, positions, face_indices, face_counts)) {
245 return existing_mesh;
246 }
247
248 const bool poly_counts_match = existing_mesh ? face_counts.size() == existing_mesh->faces_num :
249 false;
250 const bool position_counts_match = existing_mesh ? positions.size() == existing_mesh->verts_num :
251 false;
252
253 Mesh *active_mesh = nullptr;
254 if (!position_counts_match || !poly_counts_match) {
256 existing_mesh, positions.size(), 0, face_counts.size(), face_indices.size());
257 }
258 else {
259 active_mesh = existing_mesh;
260 }
261
262 MutableSpan<float3> vert_positions = active_mesh->vert_positions_for_write();
263 vert_positions.copy_from(Span(positions.data(), positions.size()).cast<float3>());
264
265 if (params.read_flags & MOD_MESHSEQ_READ_COLOR) {
266 if (active_mesh != existing_mesh) {
267 /* Clear the primvar map to force attributes to be reloaded. */
268 this->primvar_time_varying_map_.clear();
269 }
270 apply_primvars_to_mesh(active_mesh, params.motion_sample_time);
271 }
272
273 return active_mesh;
274}
275
277{
278 for (const bool animating_flag : primvar_time_varying_map_.values()) {
279 if (animating_flag) {
280 return true;
281 }
282 }
283
284 if (prim_.IsA<pxr::UsdGeomCapsule>()) {
285 pxr::UsdGeomCapsule geom(prim_);
286 return (geom.GetAxisAttr().ValueMightBeTimeVarying() ||
287 geom.GetHeightAttr().ValueMightBeTimeVarying() ||
288 geom.GetRadiusAttr().ValueMightBeTimeVarying());
289 }
290
291 if (prim_.IsA<pxr::UsdGeomCylinder>()) {
292 pxr::UsdGeomCylinder geom(prim_);
293 return (geom.GetAxisAttr().ValueMightBeTimeVarying() ||
294 geom.GetHeightAttr().ValueMightBeTimeVarying() ||
295 geom.GetRadiusAttr().ValueMightBeTimeVarying());
296 }
297
298 if (prim_.IsA<pxr::UsdGeomCone>()) {
299 pxr::UsdGeomCone geom(prim_);
300 return (geom.GetAxisAttr().ValueMightBeTimeVarying() ||
301 geom.GetHeightAttr().ValueMightBeTimeVarying() ||
302 geom.GetRadiusAttr().ValueMightBeTimeVarying());
303 }
304
305 if (prim_.IsA<pxr::UsdGeomCube>()) {
306 pxr::UsdGeomCube geom(prim_);
307 return geom.GetSizeAttr().ValueMightBeTimeVarying();
308 }
309
310 if (prim_.IsA<pxr::UsdGeomSphere>()) {
311 pxr::UsdGeomSphere geom(prim_);
312 return geom.GetRadiusAttr().ValueMightBeTimeVarying();
313 }
314
316 RPT_ERROR,
317 "Unhandled Gprim type: %s (%s)",
318 prim_.GetTypeName().GetText(),
319 prim_.GetPath().GetText());
320 return false;
321}
322
323} // namespace blender::io::usd
void BKE_id_attributes_default_color_set(struct ID *id, const char *name)
Definition attribute.cc:994
void BKE_id_attributes_active_color_set(struct ID *id, const char *name)
Definition attribute.cc:965
Mesh * BKE_mesh_new_nomain_from_template(const Mesh *me_src, int verts_num, int edges_num, int faces_num, int corners_num)
void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob)
Mesh * BKE_mesh_add(Main *bmain, const char *name)
General operations, lookup, etc. for blender objects.
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ CD_PROP_COLOR
@ MOD_MESHSEQ_READ_COLOR
Object is a sort of wrapper for general info.
@ OB_MESH
void clear()
Definition BLI_map.hh:989
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
ValueIterator values() const
Definition BLI_map.hh:846
bool contains(const Key &key) const
Definition BLI_map.hh:329
const USDImportParams & import_params_
const std::string & name() const
USDShapeReader(const pxr::UsdPrim &prim, const USDImportParams &import_params, const ImportSettings &settings)
void read_geometry(bke::GeometrySet &, USDMeshReadParams, const char **) override
void read_object_data(Main *bmain, double motionSampleTime) override
void create_object(Main *bmain, double) override
void read_object_data(Main *bmain, double motionSampleTime) override
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void mesh_smooth_set(Mesh &mesh, bool use_smooth, bool keep_sharp_edges=false)
void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, bool select_new_edges)
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 read_generic_mesh_primvar(Mesh *mesh, const pxr::UsdGeomPrimvar &primvar, const double motionSampleTime, const bool is_left_handed)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal)
int faces_num
int verts_num
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)