[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