Blender V4.5
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_geometry_set.hh"
6#include "BKE_lib_id.hh"
7#include "BKE_mesh.hh"
8#include "BKE_object.hh"
9#include "BKE_report.hh"
10
11#include "DNA_modifier_types.h"
12#include "DNA_object_types.h"
14
16#include "usd_mesh_utils.hh"
17#include "usd_reader_shape.hh"
18
19#include <pxr/usd/usdGeom/capsule.h>
20#include <pxr/usd/usdGeom/capsule_1.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/cylinder_1.h>
25#include <pxr/usd/usdGeom/plane.h>
26#include <pxr/usd/usdGeom/sphere.h>
27#include <pxr/usdImaging/usdImaging/capsuleAdapter.h>
28#include <pxr/usdImaging/usdImaging/coneAdapter.h>
29#include <pxr/usdImaging/usdImaging/cubeAdapter.h>
30#include <pxr/usdImaging/usdImaging/cylinderAdapter.h>
31#include <pxr/usdImaging/usdImaging/planeAdapter.h>
32#include <pxr/usdImaging/usdImaging/sphereAdapter.h>
33
34namespace blender::io::usd {
35
37 const USDImportParams &import_params,
38 const ImportSettings &settings)
39 : USDGeomReader(prim, import_params, settings)
40{
41}
42
44{
45 Mesh *mesh = BKE_mesh_add(bmain, name_.c_str());
47 object_->data = mesh;
48}
49
50void USDShapeReader::read_object_data(Main *bmain, double motionSampleTime)
51{
52 const USDMeshReadParams params = create_mesh_read_params(motionSampleTime,
53 import_params_.mesh_read_flag);
54 Mesh *mesh = (Mesh *)object_->data;
55 Mesh *read_mesh = this->read_mesh(mesh, params, nullptr);
56
57 if (read_mesh != mesh) {
58 BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_);
59 if (is_time_varying()) {
61 }
62 }
63
64 USDXformReader::read_object_data(bmain, motionSampleTime);
65}
66
67template<typename Adapter>
68void USDShapeReader::read_values(const double motionSampleTime,
69 pxr::VtVec3fArray &positions,
70 pxr::VtIntArray &face_indices,
71 pxr::VtIntArray &face_counts) const
72{
73 Adapter adapter;
74 pxr::VtValue points_val = adapter.GetPoints(prim_, motionSampleTime);
75
76 if (points_val.IsHolding<pxr::VtVec3fArray>()) {
77 positions = points_val.Get<pxr::VtVec3fArray>();
78 }
79
80 pxr::VtValue topology_val = adapter.GetTopology(prim_, pxr::SdfPath(), motionSampleTime);
81
82 if (topology_val.IsHolding<pxr::HdMeshTopology>()) {
83 const pxr::HdMeshTopology &topology = topology_val.Get<pxr::HdMeshTopology>();
84 face_counts = topology.GetFaceVertexCounts();
85 face_indices = topology.GetFaceVertexIndices();
86 }
87}
88
89bool USDShapeReader::read_mesh_values(double motionSampleTime,
90 pxr::VtVec3fArray &positions,
91 pxr::VtIntArray &face_indices,
92 pxr::VtIntArray &face_counts) const
93{
94 if (prim_.IsA<pxr::UsdGeomCapsule>() || prim_.IsA<pxr::UsdGeomCapsule_1>()) {
95 read_values<pxr::UsdImagingCapsuleAdapter>(
96 motionSampleTime, positions, face_indices, face_counts);
97 return true;
98 }
99
100 if (prim_.IsA<pxr::UsdGeomCylinder>() || prim_.IsA<pxr::UsdGeomCylinder_1>()) {
101 read_values<pxr::UsdImagingCylinderAdapter>(
102 motionSampleTime, positions, face_indices, face_counts);
103 return true;
104 }
105
106 if (prim_.IsA<pxr::UsdGeomCone>()) {
107 read_values<pxr::UsdImagingConeAdapter>(
108 motionSampleTime, positions, face_indices, face_counts);
109 return true;
110 }
111
112 if (prim_.IsA<pxr::UsdGeomCube>()) {
113 read_values<pxr::UsdImagingCubeAdapter>(
114 motionSampleTime, positions, face_indices, face_counts);
115 return true;
116 }
117
118 if (prim_.IsA<pxr::UsdGeomSphere>()) {
119 read_values<pxr::UsdImagingSphereAdapter>(
120 motionSampleTime, positions, face_indices, face_counts);
121 return true;
122 }
123
124 if (prim_.IsA<pxr::UsdGeomPlane>()) {
125 read_values<pxr::UsdImagingPlaneAdapter>(
126 motionSampleTime, positions, face_indices, face_counts);
127 return true;
128 }
129
131 RPT_ERROR,
132 "Unhandled Gprim type: %s (%s)",
133 prim_.GetTypeName().GetText(),
134 prim_.GetPath().GetText());
135 return false;
136}
137
138Mesh *USDShapeReader::read_mesh(Mesh *existing_mesh,
140 const char ** /*r_err_str*/)
141{
142 if (!prim_) {
143 return existing_mesh;
144 }
145
146 pxr::VtIntArray usd_face_indices;
147 pxr::VtIntArray usd_face_counts;
148
149 /* Should have a good set of data by this point-- copy over. */
150 Mesh *active_mesh = mesh_from_prim(existing_mesh, params, usd_face_indices, usd_face_counts);
151
152 if (active_mesh == existing_mesh) {
153 return existing_mesh;
154 }
155
156 Span<int> face_indices = Span(usd_face_indices.cdata(), usd_face_indices.size());
157 Span<int> face_counts = Span(usd_face_counts.cdata(), usd_face_counts.size());
158
159 MutableSpan<int> face_offsets = active_mesh->face_offsets_for_write();
160 for (const int i : IndexRange(active_mesh->faces_num)) {
161 face_offsets[i] = face_counts[i];
162 }
164
165 /* Don't smooth-shade cubes; we're not worrying about sharpness for Gprims. */
166 bke::mesh_smooth_set(*active_mesh, !prim_.IsA<pxr::UsdGeomCube>());
167
168 MutableSpan<int> corner_verts = active_mesh->corner_verts_for_write();
169 for (const int i : corner_verts.index_range()) {
170 corner_verts[i] = face_indices[i];
171 }
172
173 bke::mesh_calc_edges(*active_mesh, false, false);
174 return active_mesh;
175}
176
179 const char **r_err_str)
180{
181 Mesh *existing_mesh = geometry_set.get_mesh_for_write();
182 Mesh *new_mesh = read_mesh(existing_mesh, params, r_err_str);
183
184 if (new_mesh != existing_mesh) {
185 geometry_set.replace_mesh(new_mesh);
186 }
187}
188
189void USDShapeReader::apply_primvars_to_mesh(Mesh *mesh, const double motionSampleTime) const
190{
191 /* TODO: also handle the displayOpacity primvar. */
192 if (!mesh || !prim_) {
193 return;
194 }
195
196 pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(prim_);
197 std::vector<pxr::UsdGeomPrimvar> primvars = pv_api.GetPrimvarsWithValues();
198
199 pxr::TfToken active_color_name;
200
201 for (const pxr::UsdGeomPrimvar &pv : primvars) {
202 const pxr::SdfValueTypeName pv_type = pv.GetTypeName();
203 if (!pv_type.IsArray()) {
204 continue; /* Skip non-array primvar attributes. */
205 }
206
207 const pxr::TfToken name = pxr::UsdGeomPrimvar::StripPrimvarsName(pv.GetPrimvarName());
208
209 /* Skip reading primvars that have been read before and are not time varying. */
210 if (primvar_time_varying_map_.contains(name) && !primvar_time_varying_map_.lookup(name)) {
211 continue;
212 }
213
214 const std::optional<eCustomDataType> type = convert_usd_type_to_blender(pv_type);
215 if (type == CD_PROP_COLOR) {
216 /* Set the active color name to 'displayColor', if a color primvar
217 * with this name exists. Otherwise, use the name of the first
218 * color primvar we find for the active color. */
219 if (active_color_name.IsEmpty() || name == usdtokens::displayColor) {
220 active_color_name = name;
221 }
222 }
223
224 read_generic_mesh_primvar(mesh, pv, motionSampleTime, false);
225
226 /* Record whether the primvar attribute might be time varying. */
227 if (!primvar_time_varying_map_.contains(name)) {
228 primvar_time_varying_map_.add(name, pv.ValueMightBeTimeVarying());
229 }
230 }
231
232 if (!active_color_name.IsEmpty()) {
233 BKE_id_attributes_default_color_set(&mesh->id, active_color_name.GetText());
234 BKE_id_attributes_active_color_set(&mesh->id, active_color_name.GetText());
235 }
236}
237
238Mesh *USDShapeReader::mesh_from_prim(Mesh *existing_mesh,
240 pxr::VtIntArray &face_indices,
241 pxr::VtIntArray &face_counts) const
242{
243 pxr::VtVec3fArray positions;
244
245 if (!read_mesh_values(params.motion_sample_time, positions, face_indices, face_counts)) {
246 return existing_mesh;
247 }
248
249 const bool poly_counts_match = existing_mesh ? face_counts.size() == existing_mesh->faces_num :
250 false;
251 const bool position_counts_match = existing_mesh ? positions.size() == existing_mesh->verts_num :
252 false;
253
254 Mesh *active_mesh = nullptr;
255 if (!position_counts_match || !poly_counts_match) {
257 existing_mesh, positions.size(), 0, face_counts.size(), face_indices.size());
258 }
259 else {
260 active_mesh = existing_mesh;
261 }
262
263 MutableSpan<float3> vert_positions = active_mesh->vert_positions_for_write();
264 vert_positions.copy_from(Span(positions.cdata(), positions.size()).cast<float3>());
265
266 if (params.read_flags & MOD_MESHSEQ_READ_COLOR) {
267 if (active_mesh != existing_mesh) {
268 /* Clear the primvar map to force attributes to be reloaded. */
269 this->primvar_time_varying_map_.clear();
270 }
271 apply_primvars_to_mesh(active_mesh, params.motion_sample_time);
272 }
273
274 return active_mesh;
275}
276
278{
279 for (const bool animating_flag : primvar_time_varying_map_.values()) {
280 if (animating_flag) {
281 return true;
282 }
283 }
284
285 if (prim_.IsA<pxr::UsdGeomCapsule>()) {
286 pxr::UsdGeomCapsule geom(prim_);
287 return (geom.GetAxisAttr().ValueMightBeTimeVarying() ||
288 geom.GetHeightAttr().ValueMightBeTimeVarying() ||
289 geom.GetRadiusAttr().ValueMightBeTimeVarying());
290 }
291
292 if (prim_.IsA<pxr::UsdGeomCapsule_1>()) {
293 pxr::UsdGeomCapsule_1 geom(prim_);
294 return (geom.GetAxisAttr().ValueMightBeTimeVarying() ||
295 geom.GetHeightAttr().ValueMightBeTimeVarying() ||
296 geom.GetRadiusTopAttr().ValueMightBeTimeVarying() ||
297 geom.GetRadiusBottomAttr().ValueMightBeTimeVarying());
298 }
299
300 if (prim_.IsA<pxr::UsdGeomCylinder>()) {
301 pxr::UsdGeomCylinder geom(prim_);
302 return (geom.GetAxisAttr().ValueMightBeTimeVarying() ||
303 geom.GetHeightAttr().ValueMightBeTimeVarying() ||
304 geom.GetRadiusAttr().ValueMightBeTimeVarying());
305 }
306
307 if (prim_.IsA<pxr::UsdGeomCylinder_1>()) {
308 pxr::UsdGeomCylinder_1 geom(prim_);
309 return (geom.GetAxisAttr().ValueMightBeTimeVarying() ||
310 geom.GetHeightAttr().ValueMightBeTimeVarying() ||
311 geom.GetRadiusTopAttr().ValueMightBeTimeVarying() ||
312 geom.GetRadiusBottomAttr().ValueMightBeTimeVarying());
313 }
314
315 if (prim_.IsA<pxr::UsdGeomCone>()) {
316 pxr::UsdGeomCone geom(prim_);
317 return (geom.GetAxisAttr().ValueMightBeTimeVarying() ||
318 geom.GetHeightAttr().ValueMightBeTimeVarying() ||
319 geom.GetRadiusAttr().ValueMightBeTimeVarying());
320 }
321
322 if (prim_.IsA<pxr::UsdGeomCube>()) {
323 pxr::UsdGeomCube geom(prim_);
324 return geom.GetSizeAttr().ValueMightBeTimeVarying();
325 }
326
327 if (prim_.IsA<pxr::UsdGeomSphere>()) {
328 pxr::UsdGeomSphere geom(prim_);
329 return geom.GetRadiusAttr().ValueMightBeTimeVarying();
330 }
331
332 if (prim_.IsA<pxr::UsdGeomPlane>()) {
333 pxr::UsdGeomPlane geom(prim_);
334 return (geom.GetWidthAttr().ValueMightBeTimeVarying() ||
335 geom.GetLengthAttr().ValueMightBeTimeVarying() ||
336 geom.GetAxisAttr().ValueMightBeTimeVarying());
337 }
338
340 RPT_ERROR,
341 "Unhandled Gprim type: %s (%s)",
342 prim_.GetTypeName().GetText(),
343 prim_.GetPath().GetText());
344 return false;
345}
346
347} // namespace blender::io::usd
void BKE_id_attributes_default_color_set(struct ID *id, std::optional< blender::StringRef > name)
void BKE_id_attributes_active_color_set(struct ID *id, std::optional< blender::StringRef > name)
Definition attribute.cc:986
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
struct Mesh Mesh
@ MOD_MESHSEQ_READ_COLOR
Object is a sort of wrapper for general info.
@ OB_MESH
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
bool contains(const Key &key) const
Definition BLI_map.hh:353
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
USDGeomReader(const pxr::UsdPrim &prim, const USDImportParams &import_params, const ImportSettings &settings)
const pxr::UsdPrim & prim() const
const USDImportParams & import_params_
void create_object(Main *bmain) override
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 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)
VecBase< float, 3 > float3
const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal)
int faces_num
int verts_num
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
i
Definition text_draw.cc:230