[Bf-blender-cvs] [915d529d4d4] sybren-usd: USD: reworked mesh writing code

Sybren A. Stüvel noreply at git.blender.org
Fri Jun 28 12:00:26 CEST 2019


Commit: 915d529d4d4f0d580805c0ca13da22e4a82a6f03
Author: Sybren A. Stüvel
Date:   Fri Jun 28 11:37:36 2019 +0200
Branches: sybren-usd
https://developer.blender.org/rB915d529d4d4f0d580805c0ca13da22e4a82a6f03

USD: reworked mesh writing code

The geometry and material handling is now done in separate functions, and
has been slightly optimised for clarity.

===================================================================

M	source/blender/usd/intern/usd_writer_mesh.cc
M	source/blender/usd/intern/usd_writer_mesh.h

===================================================================

diff --git a/source/blender/usd/intern/usd_writer_mesh.cc b/source/blender/usd/intern/usd_writer_mesh.cc
index 42e46d9f934..2ca8dd064d8 100644
--- a/source/blender/usd/intern/usd_writer_mesh.cc
+++ b/source/blender/usd/intern/usd_writer_mesh.cc
@@ -22,7 +22,7 @@ void USDGenericMeshWriter::do_write(HierarchyContext &context)
 {
   Object *object_eval = context.object;
   bool needsfree = false;
-  struct Mesh *mesh = get_export_mesh(object_eval, needsfree);
+  Mesh *mesh = get_export_mesh(object_eval, needsfree);
 
   if (mesh == NULL) {
     printf("USD-\033[31mSKIPPING\033[0m object %s  type=%d mesh = NULL\n",
@@ -46,12 +46,12 @@ void USDGenericMeshWriter::do_write(HierarchyContext &context)
   }
 }
 
-void USDGenericMeshWriter::free_export_mesh(struct Mesh *mesh)
+void USDGenericMeshWriter::free_export_mesh(Mesh *mesh)
 {
   BKE_id_free(NULL, mesh);
 }
 
-void USDGenericMeshWriter::write_mesh(HierarchyContext &context, struct Mesh *mesh)
+void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
 {
   pxr::UsdTimeCode timecode = get_export_time_code();
   // printf("USD-\033[32mexporting\033[0m mesh  %s → %s  mesh = %p\n",
@@ -61,26 +61,49 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, struct Mesh *me
 
   pxr::UsdGeomMesh usd_mesh = pxr::UsdGeomMesh::Define(stage, usd_path_);
 
-  const MVert *verts = mesh->mvert;
+  // USD structures for mesh data.
+  pxr::VtArray<pxr::GfVec3f> usd_points;
+  pxr::VtIntArray usd_face_vertex_counts, usd_face_indices;
+  std::map<short, pxr::VtIntArray> usd_face_groups;
+
+  get_geometry_data(mesh, usd_points, usd_face_vertex_counts, usd_face_indices, usd_face_groups);
+
+  usd_mesh.CreatePointsAttr().Set(usd_points, timecode);
+  usd_mesh.CreateFaceVertexCountsAttr().Set(usd_face_vertex_counts, timecode);
+  usd_mesh.CreateFaceVertexIndicesAttr().Set(usd_face_indices, timecode);
+
+  // TODO(Sybren): figure out what happens when the face groups change.
+  if (frame_has_been_written_) {
+    return;
+  }
+
+  assign_materials(context, usd_mesh, usd_face_groups);
+}
+
+void USDGenericMeshWriter::get_geometry_data(const Mesh *mesh,
+                                             pxr::VtArray<pxr::GfVec3f> &usd_points,
+                                             pxr::VtIntArray &usd_face_vertex_counts,
+                                             pxr::VtIntArray &usd_face_indices,
+                                             std::map<short, pxr::VtIntArray> &usd_face_groups)
+{
+  /* Only construct face groups (a.k.a. geometry subsets) when we need them for material
+   * assignments. */
+  bool construct_face_groups = mesh->totcol > 1;
+
+  usd_points.reserve(mesh->totvert);
+  usd_face_vertex_counts.reserve(mesh->totpoly);
+  usd_face_indices.reserve(mesh->totloop);
 
   // TODO(Sybren): there is probably a more C++-y way to do this, which avoids copying the entire
   // mesh to a different structure. I haven't seen the approach below in the USD exporters for
   // Maya/Houdini, but it's simple and it works for now.
-  pxr::VtArray<pxr::GfVec3f> usd_points;
-  usd_points.reserve(mesh->totvert);
+  const MVert *verts = mesh->mvert;
   for (int i = 0; i < mesh->totvert; ++i) {
     usd_points.push_back(pxr::GfVec3f(verts[i].co));
   }
-  usd_mesh.CreatePointsAttr().Set(usd_points, timecode);
-
-  pxr::VtArray<int> usd_face_vertex_counts, usd_face_indices;
-  usd_face_vertex_counts.reserve(mesh->totpoly);
-  usd_face_indices.reserve(mesh->totloop);
 
   MLoop *mloop = mesh->mloop;
   MPoly *mpoly = mesh->mpoly;
-
-  std::map<short, pxr::VtIntArray> face_groups;
   for (int i = 0; i < mesh->totpoly; ++i, ++mpoly) {
     MLoop *loop = mloop + mpoly->loopstart;
     usd_face_vertex_counts.push_back(mpoly->totloop);
@@ -88,19 +111,46 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, struct Mesh *me
       usd_face_indices.push_back(loop->v);
     }
 
-    face_groups[mpoly->mat_nr].push_back(i);
+    if (construct_face_groups) {
+      usd_face_groups[mpoly->mat_nr].push_back(i);
+    }
   }
-  usd_mesh.CreateFaceVertexCountsAttr().Set(usd_face_vertex_counts, timecode);
-  usd_mesh.CreateFaceVertexIndicesAttr().Set(usd_face_indices, timecode);
+}
 
-  // TODO(Sybren): figure out what happens when the face groups change.
-  if (frame_has_been_written_) {
+void USDGenericMeshWriter::assign_materials(
+    const HierarchyContext &context,
+    pxr::UsdGeomMesh usd_mesh,
+    const std::map<short, pxr::VtIntArray> &usd_face_groups)
+{
+  if (context.object->totcol == 0) {
     return;
   }
 
-  // Define a geometry subset per material.
+  /* Binding a material to a geometry subset isn't supported by the Hydra GL viewport yet,
+   * which is why we always bind the first material to the entire mesh. See
+   * https://github.com/PixarAnimationStudios/USD/issues/542 for more info. */
   bool mesh_material_bound = false;
-  for (auto face_group_iter : face_groups) {
+  for (short mat_num = 0; mat_num < context.object->totcol; mat_num++) {
+    Material *material = give_current_material(context.object, mat_num + 1);
+    if (material == nullptr) {
+      continue;
+    }
+
+    pxr::UsdShadeMaterial usd_material = ensure_usd_material(material);
+    usd_material.Bind(usd_mesh.GetPrim());
+    mesh_material_bound = true;
+    break;
+  }
+
+  if (!mesh_material_bound || usd_face_groups.size() < 2) {
+    /* Either all material slots were empty or there is only one material in use. As geometry
+     * subsets are only written when actually used to assign a material, and the mesh already has
+     * the material assigned, there is no need to continue. */
+    return;
+  }
+
+  // Define a geometry subset per material.
+  for (auto face_group_iter : usd_face_groups) {
     short material_number = face_group_iter.first;
     const pxr::VtIntArray &face_indices = face_group_iter.second;
 
@@ -112,21 +162,9 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, struct Mesh *me
     pxr::UsdShadeMaterial usd_material = ensure_usd_material(material);
     pxr::TfToken material_name = usd_material.GetPath().GetNameToken();
 
-    if (face_groups.size() > 1) {
-      // Only bother with writing face groups if the object has multiple materials.
-      pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(usd_mesh);
-      pxr::UsdGeomSubset usd_face_subset = api.CreateMaterialBindSubset(material_name,
-                                                                        face_indices);
-      usd_material.Bind(usd_face_subset.GetPrim());
-    }
-
-    /* Binding a material to a geometry subset isn't supported by the Hydra GL viewport yet, which
-     * is why we also bind the first material to the entire mesh. See
-     * https://github.com/PixarAnimationStudios/USD/issues/542 for more info. */
-    if (!mesh_material_bound) {
-      usd_material.Bind(usd_mesh.GetPrim());
-      mesh_material_bound = true;
-    }
+    pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(usd_mesh);
+    pxr::UsdGeomSubset usd_face_subset = api.CreateMaterialBindSubset(material_name, face_indices);
+    usd_material.Bind(usd_face_subset.GetPrim());
   }
 }
 
diff --git a/source/blender/usd/intern/usd_writer_mesh.h b/source/blender/usd/intern/usd_writer_mesh.h
index 001a09a1a88..ee3a392ff51 100644
--- a/source/blender/usd/intern/usd_writer_mesh.h
+++ b/source/blender/usd/intern/usd_writer_mesh.h
@@ -3,6 +3,8 @@
 
 #include "usd_writer_abstract.h"
 
+#include <pxr/usd/usdGeom/mesh.h>
+
 /* Writer for USD geometry. Does not assume the object is a mesh object. */
 class USDGenericMeshWriter : public USDAbstractWriter {
  public:
@@ -16,6 +18,14 @@ class USDGenericMeshWriter : public USDAbstractWriter {
 
  private:
   void write_mesh(HierarchyContext &context, Mesh *mesh);
+  void get_geometry_data(const Mesh *mesh,
+                         pxr::VtArray<pxr::GfVec3f> &usd_points,
+                         pxr::VtIntArray &usd_face_vertex_counts,
+                         pxr::VtIntArray &usd_face_indices,
+                         std::map<short, pxr::VtIntArray> &usd_face_groups);
+  void assign_materials(const HierarchyContext &context,
+                        pxr::UsdGeomMesh usd_mesh,
+                        const std::map<short, pxr::VtIntArray> &usd_face_groups);
 };
 
 class USDMeshWriter : public USDGenericMeshWriter {



More information about the Bf-blender-cvs mailing list