Blender  V3.3
usd_reader_mesh.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Adapted from the Blender Alembic importer implementation.
3  * Modifications Copyright 2021 Tangent Animation and
4  * NVIDIA Corporation. All rights reserved. */
5 
6 #include "usd_reader_mesh.h"
7 #include "usd_reader_material.h"
8 
9 #include "BKE_customdata.h"
10 #include "BKE_main.h"
11 #include "BKE_material.h"
12 #include "BKE_mesh.h"
13 #include "BKE_object.h"
14 
15 #include "BLI_math.h"
16 #include "BLI_math_geom.h"
17 #include "BLI_math_vec_types.hh"
18 #include "BLI_span.hh"
19 #include "BLI_string.h"
20 
21 #include "DNA_customdata_types.h"
22 #include "DNA_material_types.h"
23 #include "DNA_mesh_types.h"
24 #include "DNA_meshdata_types.h"
25 #include "DNA_modifier_types.h"
26 #include "DNA_object_types.h"
27 
28 #include "MEM_guardedalloc.h"
29 
30 #include <pxr/base/vt/array.h>
31 #include <pxr/base/vt/types.h>
32 #include <pxr/base/vt/value.h>
33 #include <pxr/usd/sdf/types.h>
34 #include <pxr/usd/usdGeom/mesh.h>
35 #include <pxr/usd/usdGeom/subset.h>
36 #include <pxr/usd/usdShade/materialBindingAPI.h>
37 
38 #include <iostream>
39 
40 namespace usdtokens {
41 /* Materials */
42 static const pxr::TfToken st("st", pxr::TfToken::Immortal);
43 static const pxr::TfToken UVMap("UVMap", pxr::TfToken::Immortal);
44 static const pxr::TfToken Cd("Cd", pxr::TfToken::Immortal);
45 static const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal);
46 static const pxr::TfToken normalsPrimvar("normals", pxr::TfToken::Immortal);
47 } // namespace usdtokens
48 
49 namespace utils {
50 /* Very similar to #blender::io::alembic::utils. */
51 static void build_mat_map(const Main *bmain, std::map<std::string, Material *> *r_mat_map)
52 {
53  if (r_mat_map == nullptr) {
54  return;
55  }
56 
57  Material *material = static_cast<Material *>(bmain->materials.first);
58 
59  for (; material; material = static_cast<Material *>(material->id.next)) {
60  /* We have to do this because the stored material name is coming directly from USD. */
61  (*r_mat_map)[pxr::TfMakeValidIdentifier(material->id.name + 2)] = material;
62  }
63 }
64 
65 static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim)
66 {
67  return pxr::UsdShadeMaterialBindingAPI(prim).ComputeBoundMaterial();
68 }
69 
70 /* Returns an existing Blender material that corresponds to the USD
71  * material with with the given path. Returns null if no such material
72  * exists. */
74  const pxr::SdfPath &usd_mat_path,
75  const USDImportParams &params,
76  const std::map<std::string, Material *> &mat_map,
77  const std::map<std::string, std::string> &usd_path_to_mat_name)
78 {
79  if (params.mtl_name_collision_mode == USD_MTL_NAME_COLLISION_MAKE_UNIQUE) {
80  /* Check if we've already created the Blender material with a modified name. */
81  std::map<std::string, std::string>::const_iterator path_to_name_iter =
82  usd_path_to_mat_name.find(usd_mat_path.GetAsString());
83 
84  if (path_to_name_iter != usd_path_to_mat_name.end()) {
85  std::string mat_name = path_to_name_iter->second;
86  std::map<std::string, Material *>::const_iterator mat_iter = mat_map.find(mat_name);
87  if (mat_iter != mat_map.end()) {
88  return mat_iter->second;
89  }
90  /* We can't find the Blender material which was previously created for this USD
91  * material, which should never happen. */
93  }
94  }
95  else {
96  std::string mat_name = usd_mat_path.GetName();
97  std::map<std::string, Material *>::const_iterator mat_iter = mat_map.find(mat_name);
98 
99  if (mat_iter != mat_map.end()) {
100  return mat_iter->second;
101  }
102  }
103 
104  return nullptr;
105 }
106 
107 static void assign_materials(Main *bmain,
108  Object *ob,
109  const std::map<pxr::SdfPath, int> &mat_index_map,
110  const USDImportParams &params,
111  pxr::UsdStageRefPtr stage,
112  std::map<std::string, Material *> &mat_name_to_mat,
113  std::map<std::string, std::string> &usd_path_to_mat_name)
114 {
115  if (!(stage && bmain && ob)) {
116  return;
117  }
118 
119  if (mat_index_map.size() > MAXMAT) {
120  return;
121  }
122 
123  blender::io::usd::USDMaterialReader mat_reader(params, bmain);
124 
125  for (std::map<pxr::SdfPath, int>::const_iterator it = mat_index_map.begin();
126  it != mat_index_map.end();
127  ++it) {
128 
129  Material *assigned_mat = find_existing_material(
130  it->first, params, mat_name_to_mat, usd_path_to_mat_name);
131  if (!assigned_mat) {
132  /* Blender material doesn't exist, so create it now. */
133 
134  /* Look up the USD material. */
135  pxr::UsdPrim prim = stage->GetPrimAtPath(it->first);
136  pxr::UsdShadeMaterial usd_mat(prim);
137 
138  if (!usd_mat) {
139  std::cout << "WARNING: Couldn't construct USD material from prim " << it->first
140  << std::endl;
141  continue;
142  }
143 
144  /* Add the Blender material. */
145  assigned_mat = mat_reader.add_material(usd_mat);
146 
147  if (!assigned_mat) {
148  std::cout << "WARNING: Couldn't create Blender material from USD material " << it->first
149  << std::endl;
150  continue;
151  }
152 
153  const std::string mat_name = pxr::TfMakeValidIdentifier(assigned_mat->id.name + 2);
154  mat_name_to_mat[mat_name] = assigned_mat;
155 
156  if (params.mtl_name_collision_mode == USD_MTL_NAME_COLLISION_MAKE_UNIQUE) {
157  /* Record the name of the Blender material we created for the USD material
158  * with the given path. */
159  usd_path_to_mat_name[it->first.GetAsString()] = mat_name;
160  }
161  }
162 
163  if (assigned_mat) {
164  BKE_object_material_assign_single_obdata(bmain, ob, assigned_mat, it->second);
165  }
166  else {
167  /* This shouldn't happen. */
168  std::cout << "WARNING: Couldn't assign material " << it->first << std::endl;
169  }
170  }
171  if (ob->totcol > 0) {
172  ob->actcol = 1;
173  }
174 }
175 
176 } // namespace utils
177 
178 static void *add_customdata_cb(Mesh *mesh, const char *name, const int data_type)
179 {
180  eCustomDataType cd_data_type = static_cast<eCustomDataType>(data_type);
181  void *cd_ptr;
182  CustomData *loopdata;
183  int numloops;
184 
185  /* unsupported custom data type -- don't do anything. */
186  if (!ELEM(cd_data_type, CD_MLOOPUV, CD_PROP_BYTE_COLOR)) {
187  return nullptr;
188  }
189 
190  loopdata = &mesh->ldata;
191  cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name);
192  if (cd_ptr != nullptr) {
193  /* layer already exists, so just return it. */
194  return cd_ptr;
195  }
196 
197  /* Create a new layer. */
198  numloops = mesh->totloop;
199  cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, nullptr, numloops, name);
200  return cd_ptr;
201 }
202 
203 namespace blender::io::usd {
204 
205 USDMeshReader::USDMeshReader(const pxr::UsdPrim &prim,
206  const USDImportParams &import_params,
207  const ImportSettings &settings)
208  : USDGeomReader(prim, import_params, settings),
209  mesh_prim_(prim),
210  is_left_handed_(false),
211  has_uvs_(false),
212  is_time_varying_(false),
213  is_initial_load_(false)
214 {
215 }
216 
217 void USDMeshReader::create_object(Main *bmain, const double /* motionSampleTime */)
218 {
219  Mesh *mesh = BKE_mesh_add(bmain, name_.c_str());
220 
221  object_ = BKE_object_add_only_object(bmain, OB_MESH, name_.c_str());
222  object_->data = mesh;
223 }
224 
225 void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime)
226 {
227  Mesh *mesh = (Mesh *)object_->data;
228 
229  is_initial_load_ = true;
230  Mesh *read_mesh = this->read_mesh(
231  mesh, motionSampleTime, import_params_.mesh_read_flag, nullptr);
232 
233  is_initial_load_ = false;
234  if (read_mesh != mesh) {
235  /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */
236  /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */
237  uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
239  mesh->flag |= autosmooth;
240  }
241 
242  readFaceSetsSample(bmain, mesh, motionSampleTime);
243 
244  if (mesh_prim_.GetPointsAttr().ValueMightBeTimeVarying()) {
245  is_time_varying_ = true;
246  }
247 
248  if (is_time_varying_) {
250  }
251 
253  pxr::TfToken subdivScheme;
254  mesh_prim_.GetSubdivisionSchemeAttr().Get(&subdivScheme, motionSampleTime);
255 
256  if (subdivScheme == pxr::UsdGeomTokens->catmullClark) {
258  }
259  }
260 
261  USDXformReader::read_object_data(bmain, motionSampleTime);
262 }
263 
265 {
266  return static_cast<bool>(mesh_prim_);
267 }
268 
269 bool USDMeshReader::topology_changed(const Mesh *existing_mesh, const double motionSampleTime)
270 {
271  /* TODO(makowalski): Is it the best strategy to cache the mesh
272  * geometry in this function? This needs to be revisited. */
273 
274  mesh_prim_.GetFaceVertexIndicesAttr().Get(&face_indices_, motionSampleTime);
275  mesh_prim_.GetFaceVertexCountsAttr().Get(&face_counts_, motionSampleTime);
276  mesh_prim_.GetPointsAttr().Get(&positions_, motionSampleTime);
277 
278  /* TODO(makowalski): Reading normals probably doesn't belong in this function,
279  * as this is not required to determine if the topology has changed. */
280 
281  /* If 'normals' and 'primvars:normals' are both specified, the latter has precedence. */
282  pxr::UsdGeomPrimvar primvar = mesh_prim_.GetPrimvar(usdtokens::normalsPrimvar);
283  if (primvar.HasValue()) {
284  primvar.ComputeFlattened(&normals_, motionSampleTime);
285  normal_interpolation_ = primvar.GetInterpolation();
286  }
287  else {
288  mesh_prim_.GetNormalsAttr().Get(&normals_, motionSampleTime);
289  normal_interpolation_ = mesh_prim_.GetNormalsInterpolation();
290  }
291 
292  return positions_.size() != existing_mesh->totvert ||
293  face_counts_.size() != existing_mesh->totpoly ||
294  face_indices_.size() != existing_mesh->totloop;
295 }
296 
297 void USDMeshReader::read_mpolys(Mesh *mesh)
298 {
299  MPoly *mpolys = mesh->mpoly;
300  MLoop *mloops = mesh->mloop;
301 
302  int loop_index = 0;
303 
304  for (int i = 0; i < face_counts_.size(); i++) {
305  const int face_size = face_counts_[i];
306 
307  MPoly &poly = mpolys[i];
308  poly.loopstart = loop_index;
309  poly.totloop = face_size;
310  poly.mat_nr = 0;
311 
312  /* Polygons are always assumed to be smooth-shaded. If the mesh should be flat-shaded,
313  * this is encoded in custom loop normals. */
314  poly.flag |= ME_SMOOTH;
315 
316  if (is_left_handed_) {
317  int loop_end_index = loop_index + (face_size - 1);
318  for (int f = 0; f < face_size; ++f, ++loop_index) {
319  mloops[loop_index].v = face_indices_[loop_end_index - f];
320  }
321  }
322  else {
323  for (int f = 0; f < face_size; ++f, ++loop_index) {
324  mloops[loop_index].v = face_indices_[loop_index];
325  }
326  }
327  }
328 
329  BKE_mesh_calc_edges(mesh, false, false);
330 }
331 
332 void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bool load_uvs)
333 {
334  unsigned int loop_index = 0;
335  unsigned int rev_loop_index = 0;
336  unsigned int uv_index = 0;
337 
338  const CustomData *ldata = &mesh->ldata;
339 
340  struct UVSample {
341  pxr::VtVec2fArray uvs;
342  pxr::TfToken interpolation;
343  };
344 
345  std::vector<UVSample> uv_primvars(ldata->totlayer);
346 
347  if (has_uvs_) {
348  for (int layer_idx = 0; layer_idx < ldata->totlayer; layer_idx++) {
349  const CustomDataLayer *layer = &ldata->layers[layer_idx];
350  std::string layer_name = std::string(layer->name);
351  if (layer->type != CD_MLOOPUV) {
352  continue;
353  }
354 
355  pxr::TfToken uv_token;
356 
357  /* If first time seeing uv token, store in map of `<layer->uid, TfToken>`. */
358  if (uv_token_map_.find(layer_name) == uv_token_map_.end()) {
359  uv_token = pxr::TfToken(layer_name);
360  uv_token_map_.insert(std::make_pair(layer_name, uv_token));
361  }
362  else {
363  uv_token = uv_token_map_.at(layer_name);
364  }
365 
366  /* Early out if no token found, this should never happen */
367  if (uv_token.IsEmpty()) {
368  continue;
369  }
370  /* Early out if not first load and UVs aren't animated. */
371  if (!load_uvs && primvar_varying_map_.find(uv_token) != primvar_varying_map_.end() &&
372  !primvar_varying_map_.at(uv_token)) {
373  continue;
374  }
375 
376  /* Early out if mesh doesn't have primvar. */
377  if (!mesh_prim_.HasPrimvar(uv_token)) {
378  continue;
379  }
380 
381  if (pxr::UsdGeomPrimvar uv_primvar = mesh_prim_.GetPrimvar(uv_token)) {
382  uv_primvar.ComputeFlattened(&uv_primvars[layer_idx].uvs, motionSampleTime);
383  uv_primvars[layer_idx].interpolation = uv_primvar.GetInterpolation();
384  }
385  }
386  }
387 
388  for (int i = 0; i < face_counts_.size(); i++) {
389  const int face_size = face_counts_[i];
390 
391  rev_loop_index = loop_index + (face_size - 1);
392 
393  for (int f = 0; f < face_size; f++, loop_index++, rev_loop_index--) {
394 
395  for (int layer_idx = 0; layer_idx < ldata->totlayer; layer_idx++) {
396  const CustomDataLayer *layer = &ldata->layers[layer_idx];
397  if (layer->type != CD_MLOOPUV) {
398  continue;
399  }
400 
401  /* Early out if mismatched layer sizes. */
402  if (layer_idx > uv_primvars.size()) {
403  continue;
404  }
405 
406  /* Early out if no uvs loaded. */
407  if (uv_primvars[layer_idx].uvs.empty()) {
408  continue;
409  }
410 
411  const UVSample &sample = uv_primvars[layer_idx];
412 
413  if (!(ELEM(sample.interpolation,
414  pxr::UsdGeomTokens->faceVarying,
415  pxr::UsdGeomTokens->vertex))) {
416  std::cerr << "WARNING: unexpected interpolation type " << sample.interpolation
417  << " for uv " << layer->name << std::endl;
418  continue;
419  }
420 
421  /* For Vertex interpolation, use the vertex index. */
422  int usd_uv_index = sample.interpolation == pxr::UsdGeomTokens->vertex ?
423  mesh->mloop[loop_index].v :
424  loop_index;
425 
426  if (usd_uv_index >= sample.uvs.size()) {
427  std::cerr << "WARNING: out of bounds uv index " << usd_uv_index << " for uv "
428  << layer->name << " of size " << sample.uvs.size() << std::endl;
429  continue;
430  }
431 
432  MLoopUV *mloopuv = static_cast<MLoopUV *>(layer->data);
433  if (is_left_handed_) {
434  uv_index = rev_loop_index;
435  }
436  else {
437  uv_index = loop_index;
438  }
439  mloopuv[uv_index].uv[0] = sample.uvs[usd_uv_index][0];
440  mloopuv[uv_index].uv[1] = sample.uvs[usd_uv_index][1];
441  }
442  }
443  }
444 }
445 
446 void USDMeshReader::read_colors(Mesh *mesh, const double motionSampleTime)
447 {
448  if (!(mesh && mesh_prim_ && mesh->totloop > 0)) {
449  return;
450  }
451 
452  /* Early out if we read the display color before and if this attribute isn't animated. */
453  if (primvar_varying_map_.find(usdtokens::displayColor) != primvar_varying_map_.end() &&
454  !primvar_varying_map_.at(usdtokens::displayColor)) {
455  return;
456  }
457 
458  pxr::UsdGeomPrimvar color_primvar = mesh_prim_.GetDisplayColorPrimvar();
459 
460  if (!color_primvar.HasValue()) {
461  return;
462  }
463 
464  pxr::TfToken interp = color_primvar.GetInterpolation();
465 
466  if (interp == pxr::UsdGeomTokens->varying) {
467  std::cerr << "WARNING: Unsupported varying interpolation for display colors\n" << std::endl;
468  return;
469  }
470 
471  if (primvar_varying_map_.find(usdtokens::displayColor) == primvar_varying_map_.end()) {
472  bool might_be_time_varying = color_primvar.ValueMightBeTimeVarying();
473  primvar_varying_map_.insert(std::make_pair(usdtokens::displayColor, might_be_time_varying));
474  if (might_be_time_varying) {
475  is_time_varying_ = true;
476  }
477  }
478 
479  pxr::VtArray<pxr::GfVec3f> display_colors;
480 
481  if (!color_primvar.ComputeFlattened(&display_colors, motionSampleTime)) {
482  std::cerr << "WARNING: Couldn't compute display colors\n" << std::endl;
483  return;
484  }
485 
486  if ((interp == pxr::UsdGeomTokens->faceVarying && display_colors.size() != mesh->totloop) ||
487  (interp == pxr::UsdGeomTokens->vertex && display_colors.size() != mesh->totvert) ||
488  (interp == pxr::UsdGeomTokens->constant && display_colors.size() != 1) ||
489  (interp == pxr::UsdGeomTokens->uniform && display_colors.size() != mesh->totpoly)) {
490  std::cerr << "WARNING: display colors count mismatch\n" << std::endl;
491  return;
492  }
493 
494  void *cd_ptr = add_customdata_cb(mesh, "displayColors", CD_PROP_BYTE_COLOR);
495 
496  if (!cd_ptr) {
497  std::cerr << "WARNING: Couldn't add displayColors custom data.\n";
498  return;
499  }
500 
501  MLoopCol *colors = static_cast<MLoopCol *>(cd_ptr);
502 
503  mesh->mloopcol = colors;
504 
505  MPoly *poly = mesh->mpoly;
506 
507  for (int i = 0, e = mesh->totpoly; i < e; ++i, ++poly) {
508  for (int j = 0; j < poly->totloop; ++j) {
509  int loop_index = poly->loopstart + j;
510 
511  /* Default for constant varying interpolation. */
512  int usd_index = 0;
513 
514  if (interp == pxr::UsdGeomTokens->vertex) {
515  usd_index = mesh->mloop[loop_index].v;
516  }
517  else if (interp == pxr::UsdGeomTokens->faceVarying) {
518  usd_index = poly->loopstart;
519  if (is_left_handed_) {
520  usd_index += poly->totloop - 1 - j;
521  }
522  else {
523  usd_index += j;
524  }
525  }
526  else if (interp == pxr::UsdGeomTokens->uniform) {
527  /* Uniform varying uses the poly index. */
528  usd_index = i;
529  }
530 
531  if (usd_index >= display_colors.size()) {
532  continue;
533  }
534 
535  colors[loop_index].r = unit_float_to_uchar_clamp(display_colors[usd_index][0]);
536  colors[loop_index].g = unit_float_to_uchar_clamp(display_colors[usd_index][1]);
537  colors[loop_index].b = unit_float_to_uchar_clamp(display_colors[usd_index][2]);
538  colors[loop_index].a = unit_float_to_uchar_clamp(1.0);
539  }
540  }
541 }
542 
543 void USDMeshReader::read_vertex_creases(Mesh *mesh, const double motionSampleTime)
544 {
545  pxr::VtIntArray corner_indices;
546  if (!mesh_prim_.GetCornerIndicesAttr().Get(&corner_indices, motionSampleTime)) {
547  return;
548  }
549 
550  pxr::VtIntArray corner_sharpnesses;
551  if (!mesh_prim_.GetCornerSharpnessesAttr().Get(&corner_sharpnesses, motionSampleTime)) {
552  return;
553  }
554 
555  /* It is fine to have fewer indices than vertices, but never the other way other. */
556  if (corner_indices.size() > mesh->totvert) {
557  std::cerr << "WARNING: too many vertex crease for mesh " << prim_path_ << std::endl;
558  return;
559  }
560 
561  if (corner_indices.size() != corner_sharpnesses.size()) {
562  std::cerr << "WARNING: vertex crease indices and sharpnesses count mismatch for mesh "
563  << prim_path_ << std::endl;
564  return;
565  }
566 
567  float *creases = static_cast<float *>(
569 
570  for (size_t i = 0; i < corner_indices.size(); i++) {
571  creases[corner_indices[i]] = corner_sharpnesses[i];
572  }
573 }
574 
575 void USDMeshReader::process_normals_vertex_varying(Mesh *mesh)
576 {
577  if (!mesh) {
578  return;
579  }
580 
581  if (normals_.empty()) {
582  return;
583  }
584 
585  if (normals_.size() != mesh->totvert) {
586  std::cerr << "WARNING: vertex varying normals count mismatch for mesh " << prim_path_
587  << std::endl;
588  return;
589  }
590 
591  MutableSpan vert_normals{(float3 *)BKE_mesh_vertex_normals_for_write(mesh), mesh->totvert};
592  BLI_STATIC_ASSERT(sizeof(normals_[0]) == sizeof(float3), "Expected float3 normals size");
593  vert_normals.copy_from({(float3 *)normals_.data(), static_cast<int64_t>(normals_.size())});
595 }
596 
597 void USDMeshReader::process_normals_face_varying(Mesh *mesh)
598 {
599  if (normals_.empty()) {
601  return;
602  }
603 
604  /* Check for normals count mismatches to prevent crashes. */
605  if (normals_.size() != mesh->totloop) {
606  std::cerr << "WARNING: loop normal count mismatch for mesh " << mesh->id.name << std::endl;
608  return;
609  }
610 
611  mesh->flag |= ME_AUTOSMOOTH;
612 
613  long int loop_count = normals_.size();
614 
615  float(*lnors)[3] = static_cast<float(*)[3]>(
616  MEM_malloc_arrayN(loop_count, sizeof(float[3]), "USD::FaceNormals"));
617 
618  MPoly *mpoly = mesh->mpoly;
619 
620  for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) {
621  for (int j = 0; j < mpoly->totloop; j++) {
622  int blender_index = mpoly->loopstart + j;
623 
624  int usd_index = mpoly->loopstart;
625  if (is_left_handed_) {
626  usd_index += mpoly->totloop - 1 - j;
627  }
628  else {
629  usd_index += j;
630  }
631 
632  lnors[blender_index][0] = normals_[usd_index][0];
633  lnors[blender_index][1] = normals_[usd_index][1];
634  lnors[blender_index][2] = normals_[usd_index][2];
635  }
636  }
638 
639  MEM_freeN(lnors);
640 }
641 
642 void USDMeshReader::process_normals_uniform(Mesh *mesh)
643 {
644  if (normals_.empty()) {
646  return;
647  }
648 
649  /* Check for normals count mismatches to prevent crashes. */
650  if (normals_.size() != mesh->totpoly) {
651  std::cerr << "WARNING: uniform normal count mismatch for mesh " << mesh->id.name << std::endl;
653  return;
654  }
655 
656  float(*lnors)[3] = static_cast<float(*)[3]>(
657  MEM_malloc_arrayN(mesh->totloop, sizeof(float[3]), "USD::FaceNormals"));
658 
659  MPoly *mpoly = mesh->mpoly;
660 
661  for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) {
662 
663  for (int j = 0; j < mpoly->totloop; j++) {
664  int loop_index = mpoly->loopstart + j;
665  lnors[loop_index][0] = normals_[i][0];
666  lnors[loop_index][1] = normals_[i][1];
667  lnors[loop_index][2] = normals_[i][2];
668  }
669  }
670 
671  mesh->flag |= ME_AUTOSMOOTH;
673 
674  MEM_freeN(lnors);
675 }
676 
677 void USDMeshReader::read_mesh_sample(ImportSettings *settings,
678  Mesh *mesh,
679  const double motionSampleTime,
680  const bool new_mesh)
681 {
682  /* Note that for new meshes we always want to read verts and polys,
683  * regardless of the value of the read_flag, to avoid a crash downstream
684  * in code that expect this data to be there. */
685 
686  if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
687  for (int i = 0; i < positions_.size(); i++) {
688  MVert &mvert = mesh->mvert[i];
689  mvert.co[0] = positions_[i][0];
690  mvert.co[1] = positions_[i][1];
691  mvert.co[2] = positions_[i][2];
692  }
693 
694  read_vertex_creases(mesh, motionSampleTime);
695  }
696 
697  if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
698  read_mpolys(mesh);
699  if (normal_interpolation_ == pxr::UsdGeomTokens->faceVarying) {
700  process_normals_face_varying(mesh);
701  }
702  else if (normal_interpolation_ == pxr::UsdGeomTokens->uniform) {
703  process_normals_uniform(mesh);
704  }
705  else {
706  /* Default */
708  }
709  }
710 
711  /* Process point normals after reading polys. */
712  if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0 &&
713  normal_interpolation_ == pxr::UsdGeomTokens->vertex) {
714  process_normals_vertex_varying(mesh);
715  }
716 
717  if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
718  read_uvs(mesh, motionSampleTime, new_mesh);
719  }
720 
721  if ((settings->read_flag & MOD_MESHSEQ_READ_COLOR) != 0) {
722  read_colors(mesh, motionSampleTime);
723  }
724 }
725 
726 void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime,
727  MPoly *mpoly,
728  const int /* totpoly */,
729  std::map<pxr::SdfPath, int> *r_mat_map)
730 {
731  if (r_mat_map == nullptr) {
732  return;
733  }
734 
735  /* Find the geom subsets that have bound materials.
736  * We don't call #pxr::UsdShadeMaterialBindingAPI::GetMaterialBindSubsets()
737  * because this function returns only those subsets that are in the 'materialBind'
738  * family, but, in practice, applications (like Houdini) might export subsets
739  * in different families that are bound to materials.
740  * TODO(makowalski): Reassess if the above is the best approach. */
741  const std::vector<pxr::UsdGeomSubset> subsets = pxr::UsdGeomSubset::GetAllGeomSubsets(
742  mesh_prim_);
743 
744  int current_mat = 0;
745  if (!subsets.empty()) {
746  for (const pxr::UsdGeomSubset &subset : subsets) {
747 
748  pxr::UsdShadeMaterial subset_mtl = utils::compute_bound_material(subset.GetPrim());
749  if (!subset_mtl) {
750  continue;
751  }
752 
753  pxr::SdfPath subset_mtl_path = subset_mtl.GetPath();
754 
755  if (subset_mtl_path.IsEmpty()) {
756  continue;
757  }
758 
759  if (r_mat_map->find(subset_mtl_path) == r_mat_map->end()) {
760  (*r_mat_map)[subset_mtl_path] = 1 + current_mat++;
761  }
762 
763  const int mat_idx = (*r_mat_map)[subset_mtl_path] - 1;
764 
765  pxr::UsdAttribute indicesAttribute = subset.GetIndicesAttr();
766  pxr::VtIntArray indices;
767  indicesAttribute.Get(&indices, motionSampleTime);
768 
769  for (int i = 0; i < indices.size(); i++) {
770  MPoly &poly = mpoly[indices[i]];
771  poly.mat_nr = mat_idx;
772  }
773  }
774  }
775 
776  if (r_mat_map->empty()) {
777 
778  pxr::UsdShadeMaterial mtl = utils::compute_bound_material(prim_);
779  if (mtl) {
780  pxr::SdfPath mtl_path = mtl.GetPath();
781 
782  if (!mtl_path.IsEmpty()) {
783  r_mat_map->insert(std::make_pair(mtl.GetPath(), 1));
784  }
785  }
786  }
787 }
788 
789 void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double motionSampleTime)
790 {
792  return;
793  }
794 
795  std::map<pxr::SdfPath, int> mat_map;
796  assign_facesets_to_mpoly(motionSampleTime, mesh->mpoly, mesh->totpoly, &mat_map);
797  /* Build material name map if it's not built yet. */
798  if (this->settings_->mat_name_to_mat.empty()) {
800  }
802  object_,
803  mat_map,
804  this->import_params_,
805  this->prim_.GetStage(),
806  this->settings_->mat_name_to_mat,
807  this->settings_->usd_path_to_mat_name);
808 }
809 
811  const double motionSampleTime,
812  const int read_flag,
813  const char ** /* err_str */)
814 {
815  if (!mesh_prim_) {
816  return existing_mesh;
817  }
818 
819  mesh_prim_.GetOrientationAttr().Get(&orientation_);
820  if (orientation_ == pxr::UsdGeomTokens->leftHanded) {
821  is_left_handed_ = true;
822  }
823 
824  std::vector<pxr::TfToken> uv_tokens;
825 
826  /* Currently we only handle UV primvars. */
827  if (read_flag & MOD_MESHSEQ_READ_UV) {
828 
829  std::vector<pxr::UsdGeomPrimvar> primvars = mesh_prim_.GetPrimvars();
830 
831  for (pxr::UsdGeomPrimvar p : primvars) {
832 
833  pxr::TfToken name = p.GetPrimvarName();
834  pxr::SdfValueTypeName type = p.GetTypeName();
835 
836  bool is_uv = false;
837 
838  /* Assume all UVs are stored in one of these primvar types */
839  if (ELEM(type,
840  pxr::SdfValueTypeNames->TexCoord2hArray,
841  pxr::SdfValueTypeNames->TexCoord2fArray,
842  pxr::SdfValueTypeNames->TexCoord2dArray)) {
843  is_uv = true;
844  }
845  /* In some cases, the st primvar is stored as float2 values. */
846  else if (name == usdtokens::st && type == pxr::SdfValueTypeNames->Float2Array) {
847  is_uv = true;
848  }
849 
850  if (is_uv) {
851 
852  pxr::TfToken interp = p.GetInterpolation();
853 
854  if (!(ELEM(interp, pxr::UsdGeomTokens->faceVarying, pxr::UsdGeomTokens->vertex))) {
855  continue;
856  }
857 
858  uv_tokens.push_back(p.GetBaseName());
859  has_uvs_ = true;
860 
861  /* Record whether the UVs might be time varying. */
862  if (primvar_varying_map_.find(name) == primvar_varying_map_.end()) {
863  bool might_be_time_varying = p.ValueMightBeTimeVarying();
864  primvar_varying_map_.insert(std::make_pair(name, might_be_time_varying));
865  if (might_be_time_varying) {
866  is_time_varying_ = true;
867  }
868  }
869  }
870  }
871  }
872 
873  Mesh *active_mesh = existing_mesh;
874  bool new_mesh = false;
875 
876  /* TODO(makowalski): implement the optimization of only updating the mesh points when
877  * the topology is consistent, as in the Alembic importer. */
878 
879  ImportSettings settings;
880  settings.read_flag |= read_flag;
881 
882  if (topology_changed(existing_mesh, motionSampleTime)) {
883  new_mesh = true;
884  active_mesh = BKE_mesh_new_nomain_from_template(
885  existing_mesh, positions_.size(), 0, 0, face_indices_.size(), face_counts_.size());
886 
887  for (pxr::TfToken token : uv_tokens) {
888  void *cd_ptr = add_customdata_cb(active_mesh, token.GetText(), CD_MLOOPUV);
889  active_mesh->mloopuv = static_cast<MLoopUV *>(cd_ptr);
890  }
891  }
892 
893  read_mesh_sample(&settings, active_mesh, motionSampleTime, new_mesh || is_initial_load_);
894 
895  if (new_mesh) {
896  /* Here we assume that the number of materials doesn't change, i.e. that
897  * the material slots that were created when the object was loaded from
898  * USD are still valid now. */
899  size_t num_polys = active_mesh->totpoly;
900  if (num_polys > 0 && import_params_.import_materials) {
901  std::map<pxr::SdfPath, int> mat_map;
902  assign_facesets_to_mpoly(motionSampleTime, active_mesh->mpoly, num_polys, &mat_map);
903  }
904  }
905 
906  return active_mesh;
907 }
908 
909 } // namespace blender::io::usd
typedef float(TangentPoint)[2]
CustomData interface, see also DNA_customdata_types.h.
@ CD_DEFAULT
void * CustomData_add_layer_named(struct CustomData *data, int type, eCDAllocType alloctype, void *layer, int totelem, const char *name)
Definition: customdata.cc:2792
void * CustomData_get_layer_named(const struct CustomData *data, int type, const char *name)
void * CustomData_add_layer(struct CustomData *data, int type, eCDAllocType alloctype, void *layer, int totelem)
Definition: customdata.cc:2776
const CustomData_MeshMasks CD_MASK_MESH
Definition: customdata.cc:2065
General operations, lookup, etc. for materials.
void BKE_object_material_assign_single_obdata(struct Main *bmain, struct Object *ob, struct Material *ma, short act)
Definition: material.c:1052
float(* BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3]
struct Mesh * BKE_mesh_new_nomain_from_template(const struct Mesh *me_src, int verts_len, int edges_len, int tessface_len, int loops_len, int polys_len)
void BKE_mesh_nomain_to_mesh(struct Mesh *mesh_src, struct Mesh *mesh_dst, struct Object *ob, const struct CustomData_MeshMasks *mask, bool take_ownership)
struct Mesh * BKE_mesh_add(struct Main *bmain, const char *name)
Definition: mesh.cc:963
void BKE_mesh_vertex_normals_clear_dirty(struct Mesh *mesh)
void BKE_mesh_normals_tag_dirty(struct Mesh *mesh)
Definition: mesh_normals.cc:95
void BKE_mesh_set_custom_normals(struct Mesh *mesh, float(*r_custom_loopnors)[3])
void BKE_mesh_calc_edges(struct Mesh *mesh, bool keep_existing_edges, bool select_new_edges)
General operations, lookup, etc. for blender objects.
struct Object * BKE_object_add_only_object(struct Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
Definition: object.cc:2241
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_STATIC_ASSERT(a, msg)
Definition: BLI_assert.h:83
#define ELEM(...)
eCustomDataType
@ CD_PROP_BYTE_COLOR
@ CD_MLOOPUV
#define MAXMAT
@ ME_AUTOSMOOTH
@ ME_SMOOTH
@ MOD_MESHSEQ_READ_COLOR
@ MOD_MESHSEQ_READ_VERT
@ MOD_MESHSEQ_READ_UV
@ MOD_MESHSEQ_READ_POLY
Object is a sort of wrapper for general info.
@ OB_MESH
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
Material * add_material(const pxr::UsdShadeMaterial &usd_material) const
struct Mesh * read_mesh(struct Mesh *existing_mesh, double motionSampleTime, int read_flag, const char **err_str) override
void read_object_data(Main *bmain, double motionSampleTime) override
void create_object(Main *bmain, double motionSampleTime) override
USDMeshReader(const pxr::UsdPrim &prim, const USDImportParams &import_params, const ImportSettings &settings)
bool topology_changed(const Mesh *existing_mesh, double motionSampleTime) override
const ImportSettings * settings_
const USDImportParams & import_params_
const std::string & name() const
void read_object_data(Main *bmain, double motionSampleTime) override
EvaluationStage stage
Definition: deg_eval.cc:89
Material material
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix int ccl_global int * indices
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:34
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
ccl_device_inline float2 interp(const float2 &a, const float2 &b, float t)
Definition: math_float2.h:232
static const pxr::TfToken st("st", pxr::TfToken::Immortal)
static const pxr::TfToken UVMap("UVMap", pxr::TfToken::Immortal)
static const pxr::TfToken normalsPrimvar("normals", pxr::TfToken::Immortal)
static const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal)
static const pxr::TfToken Cd("Cd", pxr::TfToken::Immortal)
static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim)
static void assign_materials(Main *bmain, Object *ob, const std::map< pxr::SdfPath, int > &mat_index_map, const USDImportParams &params, pxr::UsdStageRefPtr stage, std::map< std::string, Material * > &mat_name_to_mat, std::map< std::string, std::string > &usd_path_to_mat_name)
static Material * find_existing_material(const pxr::SdfPath &usd_mat_path, const USDImportParams &params, const std::map< std::string, Material * > &mat_map, const std::map< std::string, std::string > &usd_path_to_mat_name)
static void build_mat_map(const Main *bmain, std::map< std::string, Material * > *r_mat_map)
unsigned short uint16_t
Definition: stdint.h:79
__int64 int64_t
Definition: stdint.h:89
CustomDataLayer * layers
void * next
Definition: DNA_ID.h:369
char name[66]
Definition: DNA_ID.h:378
void * first
Definition: DNA_listBase.h:31
unsigned char a
unsigned char b
unsigned char r
unsigned char g
unsigned int v
short mat_nr
float co[3]
Definition: BKE_main.h:121
ListBase materials
Definition: BKE_main.h:174
CustomData vdata
struct MVert * mvert
struct MLoopCol * mloopcol
uint16_t flag
struct MLoopUV * mloopuv
int totvert
struct MLoop * mloop
int totpoly
int totloop
struct MPoly * mpoly
CustomData ldata
void * data
bool import_subdiv
Definition: usd.h:56
char mesh_read_flag
Definition: usd.h:48
bool import_materials
Definition: usd.h:52
std::map< std::string, Material * > mat_name_to_mat
@ USD_MTL_NAME_COLLISION_MAKE_UNIQUE
Definition: usd.h:21
static void * add_customdata_cb(Mesh *mesh, const char *name, const int data_type)