From 151e8b42245c53104aabda22a85f6768fbdc2dc7 Mon Sep 17 00:00:00 2001 From: Charles Wardlaw Date: Wed, 1 Mar 2023 11:53:02 -0500 Subject: [PATCH 1/5] Porting forward the Colors patch from https://archive.blender.org/developer/D16103 --- source/blender/editors/io/io_usd.c | 4 +- .../blender/io/usd/intern/usd_reader_mesh.cc | 182 ++++++++++++------ .../blender/io/usd/intern/usd_reader_mesh.h | 8 +- .../blender/io/usd/intern/usd_writer_mesh.cc | 72 ++++++- .../blender/io/usd/intern/usd_writer_mesh.h | 9 + 5 files changed, 214 insertions(+), 61 deletions(-) diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index 2b2798986f9..b3e11bb6f25 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -326,7 +326,7 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "overwrite_textures", false, "Overwrite Textures", - "Allow overwriting existing texture files when exporting textures"); + "Overwrite existing files when exporting textures"); RNA_def_boolean(ot->srna, "relative_paths", @@ -612,7 +612,7 @@ void WM_OT_usd_import(struct wmOperatorType *ot) RNA_def_boolean(ot->srna, "read_mesh_uvs", true, "UV Coordinates", "Read mesh UV coordinates"); RNA_def_boolean( - ot->srna, "read_mesh_colors", false, "Color Attributes", "Read mesh color attributes"); + ot->srna, "read_mesh_colors", true, "Color Attributes", "Read mesh color attributes"); RNA_def_string(ot->srna, "prim_path_mask", diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index cd452f972b4..ace59ceb1cc 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -25,6 +25,9 @@ #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" +#include "DNA_windowmanager_types.h" + +#include "WM_api.h" #include "MEM_guardedalloc.h" @@ -35,6 +38,7 @@ #include #include #include +#include #include #include @@ -412,100 +416,154 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo } } -void USDMeshReader::read_colors(Mesh *mesh, const double motionSampleTime) +void USDMeshReader::read_color_data_all(Mesh *mesh, const double motionSampleTime) { if (!(mesh && mesh_prim_ && mesh->totloop > 0)) { return; } - /* Early out if we read the display color before and if this attribute isn't animated. */ - if (primvar_varying_map_.find(usdtokens::displayColor) != primvar_varying_map_.end() && - !primvar_varying_map_.at(usdtokens::displayColor)) { + pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(mesh_prim_); + std::vector primvars = pv_api.GetPrimvarsWithValues(); + + /* Convert color primvars to custom layer data. */ + for (pxr::UsdGeomPrimvar &pv : primvars) { + + pxr::SdfValueTypeName type = pv.GetTypeName(); + + if (!ELEM(type, + pxr::SdfValueTypeNames->Color3hArray, + pxr::SdfValueTypeNames->Color3fArray, + pxr::SdfValueTypeNames->Color3dArray)) { + continue; + } + + pxr::TfToken name = pv.GetPrimvarName(); + + /* Skip if we read this primvar before and it isn't animated. */ + const std::map::const_iterator is_animated_iter = + primvar_varying_map_.find(name); + if (is_animated_iter != primvar_varying_map_.end() && !is_animated_iter->second) { + continue; + } + + read_color_data(mesh, pv, motionSampleTime); + } +} + +void USDMeshReader::read_color_data(Mesh *mesh, + const pxr::UsdGeomPrimvar &color_primvar, + const double motionSampleTime) +{ + if (!(mesh && color_primvar && color_primvar.HasValue())) { return; } - pxr::UsdGeomPrimvar color_primvar = mesh_prim_.GetDisplayColorPrimvar(); - - if (!color_primvar.HasValue()) { - return; - } - - pxr::TfToken interp = color_primvar.GetInterpolation(); - - if (interp == pxr::UsdGeomTokens->varying) { - std::cerr << "WARNING: Unsupported varying interpolation for display colors\n" << std::endl; - return; - } - - if (primvar_varying_map_.find(usdtokens::displayColor) == primvar_varying_map_.end()) { + if (primvar_varying_map_.find(color_primvar.GetPrimvarName()) == primvar_varying_map_.end()) { bool might_be_time_varying = color_primvar.ValueMightBeTimeVarying(); - primvar_varying_map_.insert(std::make_pair(usdtokens::displayColor, might_be_time_varying)); + primvar_varying_map_.insert( + std::make_pair(color_primvar.GetPrimvarName(), might_be_time_varying)); if (might_be_time_varying) { is_time_varying_ = true; } } - pxr::VtArray display_colors; + pxr::VtArray usd_colors; - if (!color_primvar.ComputeFlattened(&display_colors, motionSampleTime)) { - std::cerr << "WARNING: Couldn't compute display colors\n" << std::endl; + if (!color_primvar.ComputeFlattened(&usd_colors, motionSampleTime)) { + WM_reportf(RPT_WARNING, + "USD Import: couldn't compute values for color attribute '%s'", + color_primvar.GetName().GetText()); return; } - if ((interp == pxr::UsdGeomTokens->faceVarying && display_colors.size() != mesh->totloop) || - (interp == pxr::UsdGeomTokens->vertex && display_colors.size() != mesh->totvert) || - (interp == pxr::UsdGeomTokens->constant && display_colors.size() != 1) || - (interp == pxr::UsdGeomTokens->uniform && display_colors.size() != mesh->totpoly)) { - std::cerr << "WARNING: display colors count mismatch\n" << std::endl; + pxr::TfToken interp = color_primvar.GetInterpolation(); + + if ((interp == pxr::UsdGeomTokens->faceVarying && usd_colors.size() != mesh->totloop) || + (interp == pxr::UsdGeomTokens->varying && usd_colors.size() != mesh->totloop) || + (interp == pxr::UsdGeomTokens->vertex && usd_colors.size() != mesh->totvert) || + (interp == pxr::UsdGeomTokens->constant && usd_colors.size() != 1) || + (interp == pxr::UsdGeomTokens->uniform && usd_colors.size() != mesh->totpoly)) { + WM_reportf(RPT_WARNING, + "USD Import: color attribute value '%s' count inconsistent with interpolation type", + color_primvar.GetName().GetText()); return; } - void *cd_ptr = add_customdata_cb(mesh, "displayColors", CD_PROP_BYTE_COLOR); + const StringRef color_primvar_name(color_primvar.GetBaseName().GetString()); + bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); - if (!cd_ptr) { - std::cerr << "WARNING: Couldn't add displayColors custom data.\n"; + eAttrDomain color_domain = ATTR_DOMAIN_POINT; + + if (ELEM(interp, + pxr::UsdGeomTokens->varying, + pxr::UsdGeomTokens->faceVarying, + pxr::UsdGeomTokens->uniform)) { + color_domain = ATTR_DOMAIN_CORNER; + } + + bke::SpanAttributeWriter color_data; + color_data = attributes.lookup_or_add_for_write_only_span(color_primvar_name, + color_domain); + if (!color_data) { + WM_reportf(RPT_WARNING, + "USD Import: couldn't add color attribute '%s'", + color_primvar.GetBaseName().GetText()); return; } - MLoopCol *colors = static_cast(cd_ptr); + /* Check for situations that allow for a straight-forward copy by index. */ + if ((ELEM(interp, pxr::UsdGeomTokens->vertex)) || + (color_domain == ATTR_DOMAIN_CORNER && !is_left_handed_)) { + for (int i = 0; i < usd_colors.size(); i++) { + ColorGeometry4f color = ColorGeometry4f( + usd_colors[i][0], usd_colors[i][1], usd_colors[i][2], 1.0f); + color_data.span[i] = color; + } + } - const Span polys = mesh->polys(); - const Span loops = mesh->loops(); - for (const int i : polys.index_range()) { - const MPoly &poly = polys[i]; - for (int j = 0; j < poly.totloop; ++j) { - int loop_index = poly.loopstart + j; + /* Special case: expand uniform color into corner color. + * Uniforms in USD come through as single colors, face-varying. Since Blender does not + * support this particular combination for paintable color attributes, we convert the type + * here to make sure that the user gets the same visual result. + * */ + else if (ELEM(interp, pxr::UsdGeomTokens->uniform)) { + for (int i = 0; i < usd_colors.size(); i++) { + const ColorGeometry4f color = ColorGeometry4f( + usd_colors[i][0], usd_colors[i][1], usd_colors[i][2], 1.0f); + color_data.span[i * 4] = color; + color_data.span[i * 4 + 1] = color; + color_data.span[i * 4 + 2] = color; + color_data.span[i * 4 + 3] = color; + } + } - /* Default for constant varying interpolation. */ - int usd_index = 0; + else { + /* Loop-level data, but left-handed, requires a bit of a swizzle. */ + const Span polys = mesh->polys(); - if (interp == pxr::UsdGeomTokens->vertex) { - usd_index = loops[loop_index].v; - } - else if (interp == pxr::UsdGeomTokens->faceVarying) { - usd_index = poly.loopstart; + for (const MPoly &poly : polys) { + for (int j = 0; j < poly.totloop; ++j) { + /* Default for constant varying interpolation. */ + int usd_index = poly.loopstart; if (is_left_handed_) { usd_index += poly.totloop - 1 - j; } else { usd_index += j; } - } - else if (interp == pxr::UsdGeomTokens->uniform) { - /* Uniform varying uses the poly index. */ - usd_index = i; - } - if (usd_index >= display_colors.size()) { - continue; - } + if (usd_index >= usd_colors.size()) { + continue; + } - colors[loop_index].r = unit_float_to_uchar_clamp(display_colors[usd_index][0]); - colors[loop_index].g = unit_float_to_uchar_clamp(display_colors[usd_index][1]); - colors[loop_index].b = unit_float_to_uchar_clamp(display_colors[usd_index][2]); - colors[loop_index].a = unit_float_to_uchar_clamp(1.0); + ColorGeometry4f color = ColorGeometry4f( + usd_colors[usd_index][0], usd_colors[usd_index][1], usd_colors[usd_index][2], 1.0f); + color_data.span[usd_index] = color; + } } } + + color_data.finish(); } void USDMeshReader::read_vertex_creases(Mesh *mesh, const double motionSampleTime) @@ -676,9 +734,19 @@ void USDMeshReader::read_mesh_sample(ImportSettings *settings, read_uvs(mesh, motionSampleTime, new_mesh); } + /* Custom Data layers. */ + read_custom_data(settings, mesh, motionSampleTime); +} + +void USDMeshReader::read_custom_data(const ImportSettings *settings, + Mesh *mesh, + const double motionSampleTime) +{ if ((settings->read_flag & MOD_MESHSEQ_READ_COLOR) != 0) { - read_colors(mesh, motionSampleTime); + read_color_data_all(mesh, motionSampleTime); } + + /* TODO: Generic readers for custom data layers not listed above. */ } void USDMeshReader::assign_facesets_to_material_indices(double motionSampleTime, diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h index 0cabae2a653..a50bd097026 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.h +++ b/source/blender/io/usd/intern/usd_reader_mesh.h @@ -66,13 +66,19 @@ class USDMeshReader : public USDGeomReader { void read_mpolys(Mesh *mesh); void read_uvs(Mesh *mesh, double motionSampleTime, bool load_uvs = false); - void read_colors(Mesh *mesh, double motionSampleTime); void read_vertex_creases(Mesh *mesh, double motionSampleTime); void read_mesh_sample(ImportSettings *settings, Mesh *mesh, double motionSampleTime, bool new_mesh); + + void read_custom_data(const ImportSettings *settings, + Mesh *mesh, + double motionSampleTime); + + void read_color_data_all(Mesh *mesh, double motionSampleTime); + void read_color_data(Mesh *mesh, const pxr::UsdGeomPrimvar &color_primvar, double motionSampleTime); }; } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index 9472946e680..715fcd02064 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -14,7 +14,6 @@ #include "BLI_math_vector_types.hh" #include "BKE_attribute.h" -#include "BKE_attribute.hh" #include "BKE_customdata.h" #include "BKE_lib_id.h" #include "BKE_material.h" @@ -32,6 +31,8 @@ #include "DNA_object_fluidsim_types.h" #include "DNA_particle_types.h" +#include "WM_api.h" + #include namespace blender::io::usd { @@ -73,6 +74,72 @@ void USDGenericMeshWriter::do_write(HierarchyContext &context) } } +void USDGenericMeshWriter::write_custom_data(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh) +{ + const bke::AttributeAccessor attributes = mesh->attributes(); + + attributes.for_all( + [&](const bke::AttributeIDRef &attribute_id, const bke::AttributeMetaData &meta_data) { + /* Color data. */ + if (ELEM(meta_data.domain, ATTR_DOMAIN_CORNER, ATTR_DOMAIN_POINT) && + ELEM(meta_data.data_type, CD_PROP_BYTE_COLOR, CD_PROP_COLOR)) { + write_color_data(mesh, usd_mesh, attribute_id, meta_data); + } + + return true; + }); +} + +void USDGenericMeshWriter::write_color_data(const Mesh *mesh, + pxr::UsdGeomMesh usd_mesh, + const bke::AttributeIDRef &attribute_id, + const bke::AttributeMetaData &meta_data) +{ + pxr::UsdTimeCode timecode = get_export_time_code(); + const std::string name = attribute_id.name(); + pxr::TfToken primvar_name(pxr::TfMakeValidIdentifier(name)); + const pxr::UsdGeomPrimvarsAPI pvApi = pxr::UsdGeomPrimvarsAPI(usd_mesh); + + /* Varying type depends on original domain. */ + const pxr::TfToken prim_varying = meta_data.domain == ATTR_DOMAIN_CORNER ? + pxr::UsdGeomTokens->faceVarying : + pxr::UsdGeomTokens->vertex; + + pxr::UsdGeomPrimvar colors_pv = pvApi.CreatePrimvar( + primvar_name, pxr::SdfValueTypeNames->Color3fArray, prim_varying); + + const VArray attribute = mesh->attributes().lookup_or_default( + attribute_id, meta_data.domain, {0.0f, 0.0f, 0.0f, 1.0f}); + + pxr::VtArray colors_data; + + /* TODO: Thread the copy, like the obj exporter. */ + switch (meta_data.domain) { + case ATTR_DOMAIN_CORNER: + for (size_t loop_idx = 0; loop_idx < mesh->totloop; loop_idx++) { + const ColorGeometry4f color = attribute.get(loop_idx); + colors_data.push_back(pxr::GfVec3f(color.r, color.g, color.b)); + } + break; + + case ATTR_DOMAIN_POINT: + for (const int point_index : attribute.index_range()) { + const ColorGeometry4f color = attribute.get(point_index); + colors_data.push_back(pxr::GfVec3f(color.r, color.g, color.b)); + } + break; + + default: + BLI_assert_msg(0, "Invalid domain for mesh color data."); + return; + } + + colors_pv.Set(colors_data, timecode); + + const pxr::UsdAttribute &prim_colors_attr = colors_pv.GetAttr(); + usd_value_writer_.SetAttribute(prim_colors_attr, pxr::VtValue(colors_data), timecode); +} + void USDGenericMeshWriter::free_export_mesh(Mesh *mesh) { BKE_id_free(nullptr, mesh); @@ -233,6 +300,9 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) if (usd_export_context_.export_params.export_uvmaps) { write_uv_maps(mesh, usd_mesh); } + + write_custom_data(mesh, usd_mesh); + if (usd_export_context_.export_params.export_normals) { write_normals(mesh, usd_mesh); } diff --git a/source/blender/io/usd/intern/usd_writer_mesh.h b/source/blender/io/usd/intern/usd_writer_mesh.h index b33152e125c..34aef4e24b0 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.h +++ b/source/blender/io/usd/intern/usd_writer_mesh.h @@ -4,6 +4,8 @@ #include "usd_writer_abstract.h" +#include "BKE_attribute.hh" + #include namespace blender::io::usd { @@ -34,6 +36,13 @@ class USDGenericMeshWriter : public USDAbstractWriter { void write_uv_maps(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); void write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); void write_surface_velocity(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); + + void write_custom_data(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); + + void write_color_data(const Mesh *mesh, + pxr::UsdGeomMesh usd_mesh, + const bke::AttributeIDRef &attribute_id, + const bke::AttributeMetaData &meta_data); }; class USDMeshWriter : public USDGenericMeshWriter { -- 2.30.2 From ec96ef0e0a05321f04666782d5c34e84115bb0f5 Mon Sep 17 00:00:00 2001 From: Charles Wardlaw Date: Wed, 1 Mar 2023 11:53:02 -0500 Subject: [PATCH 2/5] Porting forward the Colors patch from https://archive.blender.org/developer/D16103 --- source/blender/editors/io/io_usd.c | 4 +- .../blender/io/usd/intern/usd_reader_mesh.cc | 182 ++++++++++++------ .../blender/io/usd/intern/usd_reader_mesh.h | 8 +- .../blender/io/usd/intern/usd_writer_mesh.cc | 72 ++++++- .../blender/io/usd/intern/usd_writer_mesh.h | 9 + 5 files changed, 214 insertions(+), 61 deletions(-) diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index 2b2798986f9..b3e11bb6f25 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -326,7 +326,7 @@ void WM_OT_usd_export(struct wmOperatorType *ot) "overwrite_textures", false, "Overwrite Textures", - "Allow overwriting existing texture files when exporting textures"); + "Overwrite existing files when exporting textures"); RNA_def_boolean(ot->srna, "relative_paths", @@ -612,7 +612,7 @@ void WM_OT_usd_import(struct wmOperatorType *ot) RNA_def_boolean(ot->srna, "read_mesh_uvs", true, "UV Coordinates", "Read mesh UV coordinates"); RNA_def_boolean( - ot->srna, "read_mesh_colors", false, "Color Attributes", "Read mesh color attributes"); + ot->srna, "read_mesh_colors", true, "Color Attributes", "Read mesh color attributes"); RNA_def_string(ot->srna, "prim_path_mask", diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 88aeeac3314..02d30f8b3be 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -25,6 +25,9 @@ #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" +#include "DNA_windowmanager_types.h" + +#include "WM_api.h" #include "MEM_guardedalloc.h" @@ -35,6 +38,7 @@ #include #include #include +#include #include #include @@ -411,100 +415,154 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo } } -void USDMeshReader::read_colors(Mesh *mesh, const double motionSampleTime) +void USDMeshReader::read_color_data_all(Mesh *mesh, const double motionSampleTime) { if (!(mesh && mesh_prim_ && mesh->totloop > 0)) { return; } - /* Early out if we read the display color before and if this attribute isn't animated. */ - if (primvar_varying_map_.find(usdtokens::displayColor) != primvar_varying_map_.end() && - !primvar_varying_map_.at(usdtokens::displayColor)) { + pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(mesh_prim_); + std::vector primvars = pv_api.GetPrimvarsWithValues(); + + /* Convert color primvars to custom layer data. */ + for (pxr::UsdGeomPrimvar &pv : primvars) { + + pxr::SdfValueTypeName type = pv.GetTypeName(); + + if (!ELEM(type, + pxr::SdfValueTypeNames->Color3hArray, + pxr::SdfValueTypeNames->Color3fArray, + pxr::SdfValueTypeNames->Color3dArray)) { + continue; + } + + pxr::TfToken name = pv.GetPrimvarName(); + + /* Skip if we read this primvar before and it isn't animated. */ + const std::map::const_iterator is_animated_iter = + primvar_varying_map_.find(name); + if (is_animated_iter != primvar_varying_map_.end() && !is_animated_iter->second) { + continue; + } + + read_color_data(mesh, pv, motionSampleTime); + } +} + +void USDMeshReader::read_color_data(Mesh *mesh, + const pxr::UsdGeomPrimvar &color_primvar, + const double motionSampleTime) +{ + if (!(mesh && color_primvar && color_primvar.HasValue())) { return; } - pxr::UsdGeomPrimvar color_primvar = mesh_prim_.GetDisplayColorPrimvar(); - - if (!color_primvar.HasValue()) { - return; - } - - pxr::TfToken interp = color_primvar.GetInterpolation(); - - if (interp == pxr::UsdGeomTokens->varying) { - std::cerr << "WARNING: Unsupported varying interpolation for display colors\n" << std::endl; - return; - } - - if (primvar_varying_map_.find(usdtokens::displayColor) == primvar_varying_map_.end()) { + if (primvar_varying_map_.find(color_primvar.GetPrimvarName()) == primvar_varying_map_.end()) { bool might_be_time_varying = color_primvar.ValueMightBeTimeVarying(); - primvar_varying_map_.insert(std::make_pair(usdtokens::displayColor, might_be_time_varying)); + primvar_varying_map_.insert( + std::make_pair(color_primvar.GetPrimvarName(), might_be_time_varying)); if (might_be_time_varying) { is_time_varying_ = true; } } - pxr::VtArray display_colors; + pxr::VtArray usd_colors; - if (!color_primvar.ComputeFlattened(&display_colors, motionSampleTime)) { - std::cerr << "WARNING: Couldn't compute display colors\n" << std::endl; + if (!color_primvar.ComputeFlattened(&usd_colors, motionSampleTime)) { + WM_reportf(RPT_WARNING, + "USD Import: couldn't compute values for color attribute '%s'", + color_primvar.GetName().GetText()); return; } - if ((interp == pxr::UsdGeomTokens->faceVarying && display_colors.size() != mesh->totloop) || - (interp == pxr::UsdGeomTokens->vertex && display_colors.size() != mesh->totvert) || - (interp == pxr::UsdGeomTokens->constant && display_colors.size() != 1) || - (interp == pxr::UsdGeomTokens->uniform && display_colors.size() != mesh->totpoly)) { - std::cerr << "WARNING: display colors count mismatch\n" << std::endl; + pxr::TfToken interp = color_primvar.GetInterpolation(); + + if ((interp == pxr::UsdGeomTokens->faceVarying && usd_colors.size() != mesh->totloop) || + (interp == pxr::UsdGeomTokens->varying && usd_colors.size() != mesh->totloop) || + (interp == pxr::UsdGeomTokens->vertex && usd_colors.size() != mesh->totvert) || + (interp == pxr::UsdGeomTokens->constant && usd_colors.size() != 1) || + (interp == pxr::UsdGeomTokens->uniform && usd_colors.size() != mesh->totpoly)) { + WM_reportf(RPT_WARNING, + "USD Import: color attribute value '%s' count inconsistent with interpolation type", + color_primvar.GetName().GetText()); return; } - void *cd_ptr = add_customdata_cb(mesh, "displayColors", CD_PROP_BYTE_COLOR); + const StringRef color_primvar_name(color_primvar.GetBaseName().GetString()); + bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); - if (!cd_ptr) { - std::cerr << "WARNING: Couldn't add displayColors custom data.\n"; + eAttrDomain color_domain = ATTR_DOMAIN_POINT; + + if (ELEM(interp, + pxr::UsdGeomTokens->varying, + pxr::UsdGeomTokens->faceVarying, + pxr::UsdGeomTokens->uniform)) { + color_domain = ATTR_DOMAIN_CORNER; + } + + bke::SpanAttributeWriter color_data; + color_data = attributes.lookup_or_add_for_write_only_span(color_primvar_name, + color_domain); + if (!color_data) { + WM_reportf(RPT_WARNING, + "USD Import: couldn't add color attribute '%s'", + color_primvar.GetBaseName().GetText()); return; } - MLoopCol *colors = static_cast(cd_ptr); + /* Check for situations that allow for a straight-forward copy by index. */ + if ((ELEM(interp, pxr::UsdGeomTokens->vertex)) || + (color_domain == ATTR_DOMAIN_CORNER && !is_left_handed_)) { + for (int i = 0; i < usd_colors.size(); i++) { + ColorGeometry4f color = ColorGeometry4f( + usd_colors[i][0], usd_colors[i][1], usd_colors[i][2], 1.0f); + color_data.span[i] = color; + } + } - const Span polys = mesh->polys(); - const Span loops = mesh->loops(); - for (const int i : polys.index_range()) { - const MPoly &poly = polys[i]; - for (int j = 0; j < poly.totloop; ++j) { - int loop_index = poly.loopstart + j; + /* Special case: expand uniform color into corner color. + * Uniforms in USD come through as single colors, face-varying. Since Blender does not + * support this particular combination for paintable color attributes, we convert the type + * here to make sure that the user gets the same visual result. + * */ + else if (ELEM(interp, pxr::UsdGeomTokens->uniform)) { + for (int i = 0; i < usd_colors.size(); i++) { + const ColorGeometry4f color = ColorGeometry4f( + usd_colors[i][0], usd_colors[i][1], usd_colors[i][2], 1.0f); + color_data.span[i * 4] = color; + color_data.span[i * 4 + 1] = color; + color_data.span[i * 4 + 2] = color; + color_data.span[i * 4 + 3] = color; + } + } - /* Default for constant varying interpolation. */ - int usd_index = 0; + else { + /* Loop-level data, but left-handed, requires a bit of a swizzle. */ + const Span polys = mesh->polys(); - if (interp == pxr::UsdGeomTokens->vertex) { - usd_index = loops[loop_index].v; - } - else if (interp == pxr::UsdGeomTokens->faceVarying) { - usd_index = poly.loopstart; + for (const MPoly &poly : polys) { + for (int j = 0; j < poly.totloop; ++j) { + /* Default for constant varying interpolation. */ + int usd_index = poly.loopstart; if (is_left_handed_) { usd_index += poly.totloop - 1 - j; } else { usd_index += j; } - } - else if (interp == pxr::UsdGeomTokens->uniform) { - /* Uniform varying uses the poly index. */ - usd_index = i; - } - if (usd_index >= display_colors.size()) { - continue; - } + if (usd_index >= usd_colors.size()) { + continue; + } - colors[loop_index].r = unit_float_to_uchar_clamp(display_colors[usd_index][0]); - colors[loop_index].g = unit_float_to_uchar_clamp(display_colors[usd_index][1]); - colors[loop_index].b = unit_float_to_uchar_clamp(display_colors[usd_index][2]); - colors[loop_index].a = unit_float_to_uchar_clamp(1.0); + ColorGeometry4f color = ColorGeometry4f( + usd_colors[usd_index][0], usd_colors[usd_index][1], usd_colors[usd_index][2], 1.0f); + color_data.span[usd_index] = color; + } } } + + color_data.finish(); } void USDMeshReader::read_vertex_creases(Mesh *mesh, const double motionSampleTime) @@ -675,9 +733,19 @@ void USDMeshReader::read_mesh_sample(ImportSettings *settings, read_uvs(mesh, motionSampleTime, new_mesh); } + /* Custom Data layers. */ + read_custom_data(settings, mesh, motionSampleTime); +} + +void USDMeshReader::read_custom_data(const ImportSettings *settings, + Mesh *mesh, + const double motionSampleTime) +{ if ((settings->read_flag & MOD_MESHSEQ_READ_COLOR) != 0) { - read_colors(mesh, motionSampleTime); + read_color_data_all(mesh, motionSampleTime); } + + /* TODO: Generic readers for custom data layers not listed above. */ } void USDMeshReader::assign_facesets_to_material_indices(double motionSampleTime, diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h index 0cabae2a653..a50bd097026 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.h +++ b/source/blender/io/usd/intern/usd_reader_mesh.h @@ -66,13 +66,19 @@ class USDMeshReader : public USDGeomReader { void read_mpolys(Mesh *mesh); void read_uvs(Mesh *mesh, double motionSampleTime, bool load_uvs = false); - void read_colors(Mesh *mesh, double motionSampleTime); void read_vertex_creases(Mesh *mesh, double motionSampleTime); void read_mesh_sample(ImportSettings *settings, Mesh *mesh, double motionSampleTime, bool new_mesh); + + void read_custom_data(const ImportSettings *settings, + Mesh *mesh, + double motionSampleTime); + + void read_color_data_all(Mesh *mesh, double motionSampleTime); + void read_color_data(Mesh *mesh, const pxr::UsdGeomPrimvar &color_primvar, double motionSampleTime); }; } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index 6dd1ec80ae8..aca89c86175 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -14,7 +14,6 @@ #include "BLI_math_vector_types.hh" #include "BKE_attribute.h" -#include "BKE_attribute.hh" #include "BKE_customdata.h" #include "BKE_lib_id.h" #include "BKE_material.h" @@ -32,6 +31,8 @@ #include "DNA_object_fluidsim_types.h" #include "DNA_particle_types.h" +#include "WM_api.h" + #include namespace blender::io::usd { @@ -73,6 +74,72 @@ void USDGenericMeshWriter::do_write(HierarchyContext &context) } } +void USDGenericMeshWriter::write_custom_data(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh) +{ + const bke::AttributeAccessor attributes = mesh->attributes(); + + attributes.for_all( + [&](const bke::AttributeIDRef &attribute_id, const bke::AttributeMetaData &meta_data) { + /* Color data. */ + if (ELEM(meta_data.domain, ATTR_DOMAIN_CORNER, ATTR_DOMAIN_POINT) && + ELEM(meta_data.data_type, CD_PROP_BYTE_COLOR, CD_PROP_COLOR)) { + write_color_data(mesh, usd_mesh, attribute_id, meta_data); + } + + return true; + }); +} + +void USDGenericMeshWriter::write_color_data(const Mesh *mesh, + pxr::UsdGeomMesh usd_mesh, + const bke::AttributeIDRef &attribute_id, + const bke::AttributeMetaData &meta_data) +{ + pxr::UsdTimeCode timecode = get_export_time_code(); + const std::string name = attribute_id.name(); + pxr::TfToken primvar_name(pxr::TfMakeValidIdentifier(name)); + const pxr::UsdGeomPrimvarsAPI pvApi = pxr::UsdGeomPrimvarsAPI(usd_mesh); + + /* Varying type depends on original domain. */ + const pxr::TfToken prim_varying = meta_data.domain == ATTR_DOMAIN_CORNER ? + pxr::UsdGeomTokens->faceVarying : + pxr::UsdGeomTokens->vertex; + + pxr::UsdGeomPrimvar colors_pv = pvApi.CreatePrimvar( + primvar_name, pxr::SdfValueTypeNames->Color3fArray, prim_varying); + + const VArray attribute = mesh->attributes().lookup_or_default( + attribute_id, meta_data.domain, {0.0f, 0.0f, 0.0f, 1.0f}); + + pxr::VtArray colors_data; + + /* TODO: Thread the copy, like the obj exporter. */ + switch (meta_data.domain) { + case ATTR_DOMAIN_CORNER: + for (size_t loop_idx = 0; loop_idx < mesh->totloop; loop_idx++) { + const ColorGeometry4f color = attribute.get(loop_idx); + colors_data.push_back(pxr::GfVec3f(color.r, color.g, color.b)); + } + break; + + case ATTR_DOMAIN_POINT: + for (const int point_index : attribute.index_range()) { + const ColorGeometry4f color = attribute.get(point_index); + colors_data.push_back(pxr::GfVec3f(color.r, color.g, color.b)); + } + break; + + default: + BLI_assert_msg(0, "Invalid domain for mesh color data."); + return; + } + + colors_pv.Set(colors_data, timecode); + + const pxr::UsdAttribute &prim_colors_attr = colors_pv.GetAttr(); + usd_value_writer_.SetAttribute(prim_colors_attr, pxr::VtValue(colors_data), timecode); +} + void USDGenericMeshWriter::free_export_mesh(Mesh *mesh) { BKE_id_free(nullptr, mesh); @@ -233,6 +300,9 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) if (usd_export_context_.export_params.export_uvmaps) { write_uv_maps(mesh, usd_mesh); } + + write_custom_data(mesh, usd_mesh); + if (usd_export_context_.export_params.export_normals) { write_normals(mesh, usd_mesh); } diff --git a/source/blender/io/usd/intern/usd_writer_mesh.h b/source/blender/io/usd/intern/usd_writer_mesh.h index b33152e125c..34aef4e24b0 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.h +++ b/source/blender/io/usd/intern/usd_writer_mesh.h @@ -4,6 +4,8 @@ #include "usd_writer_abstract.h" +#include "BKE_attribute.hh" + #include namespace blender::io::usd { @@ -34,6 +36,13 @@ class USDGenericMeshWriter : public USDAbstractWriter { void write_uv_maps(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); void write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); void write_surface_velocity(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); + + void write_custom_data(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); + + void write_color_data(const Mesh *mesh, + pxr::UsdGeomMesh usd_mesh, + const bke::AttributeIDRef &attribute_id, + const bke::AttributeMetaData &meta_data); }; class USDMeshWriter : public USDGenericMeshWriter { -- 2.30.2 From 6897c3d3577948f3fc53d31860df570b0c40de19 Mon Sep 17 00:00:00 2001 From: Charles Wardlaw Date: Thu, 30 Mar 2023 16:36:53 -0400 Subject: [PATCH 3/5] Added in Michael's fix for displayColor. Fixed issues importing non-indexed display colors (Pixar Kitchen now loads correctly). --- .../blender/io/usd/intern/usd_reader_mesh.cc | 114 +++++++++++------- 1 file changed, 69 insertions(+), 45 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 02d30f8b3be..1a26b44e10e 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -424,10 +424,16 @@ void USDMeshReader::read_color_data_all(Mesh *mesh, const double motionSampleTim pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(mesh_prim_); std::vector primvars = pv_api.GetPrimvarsWithValues(); + pxr::TfToken active_color_name; + /* Convert color primvars to custom layer data. */ for (pxr::UsdGeomPrimvar &pv : primvars) { + if (!pv.HasValue()) { + continue; + } pxr::SdfValueTypeName type = pv.GetTypeName(); + const char *name_as_string = type.GetAsToken().GetText(); if (!ELEM(type, pxr::SdfValueTypeNames->Color3hArray, @@ -438,6 +444,13 @@ void USDMeshReader::read_color_data_all(Mesh *mesh, const double motionSampleTim pxr::TfToken name = pv.GetPrimvarName(); + /* Set the active color name to 'displayColor', if a color primvar + * with this name exists. Otherwise, use the name of the first + * color primvar we find for the active color. */ + if (active_color_name.IsEmpty() || name == usdtokens::displayColor) { + active_color_name = name; + } + /* Skip if we read this primvar before and it isn't animated. */ const std::map::const_iterator is_animated_iter = primvar_varying_map_.find(name); @@ -447,6 +460,10 @@ void USDMeshReader::read_color_data_all(Mesh *mesh, const double motionSampleTim read_color_data(mesh, pv, motionSampleTime); } + + if (!active_color_name.IsEmpty()) { + BKE_id_attributes_active_color_set(&mesh->id, active_color_name.GetText()); + } } void USDMeshReader::read_color_data(Mesh *mesh, @@ -510,54 +527,61 @@ void USDMeshReader::read_color_data(Mesh *mesh, return; } - /* Check for situations that allow for a straight-forward copy by index. */ - if ((ELEM(interp, pxr::UsdGeomTokens->vertex)) || - (color_domain == ATTR_DOMAIN_CORNER && !is_left_handed_)) { - for (int i = 0; i < usd_colors.size(); i++) { - ColorGeometry4f color = ColorGeometry4f( - usd_colors[i][0], usd_colors[i][1], usd_colors[i][2], 1.0f); - color_data.span[i] = color; - } + if (ELEM(interp, pxr::UsdGeomTokens->constant, pxr::UsdGeomTokens->uniform)) { + /* For situations where there's only a single item, flood fill the object. */ + color_data.span.fill( + ColorGeometry4f(usd_colors[0][0], usd_colors[0][1], usd_colors[0][2], 1.0f)); } - - /* Special case: expand uniform color into corner color. - * Uniforms in USD come through as single colors, face-varying. Since Blender does not - * support this particular combination for paintable color attributes, we convert the type - * here to make sure that the user gets the same visual result. - * */ - else if (ELEM(interp, pxr::UsdGeomTokens->uniform)) { - for (int i = 0; i < usd_colors.size(); i++) { - const ColorGeometry4f color = ColorGeometry4f( - usd_colors[i][0], usd_colors[i][1], usd_colors[i][2], 1.0f); - color_data.span[i * 4] = color; - color_data.span[i * 4 + 1] = color; - color_data.span[i * 4 + 2] = color; - color_data.span[i * 4 + 3] = color; - } - } - else { - /* Loop-level data, but left-handed, requires a bit of a swizzle. */ - const Span polys = mesh->polys(); - - for (const MPoly &poly : polys) { - for (int j = 0; j < poly.totloop; ++j) { - /* Default for constant varying interpolation. */ - int usd_index = poly.loopstart; - if (is_left_handed_) { - usd_index += poly.totloop - 1 - j; - } - else { - usd_index += j; - } - - if (usd_index >= usd_colors.size()) { - continue; - } - + /* Check for situations that allow for a straight-forward copy by index. */ + if ((ELEM(interp, pxr::UsdGeomTokens->vertex)) || + (color_domain == ATTR_DOMAIN_CORNER && !is_left_handed_)) { + for (int i = 0; i < usd_colors.size(); i++) { ColorGeometry4f color = ColorGeometry4f( - usd_colors[usd_index][0], usd_colors[usd_index][1], usd_colors[usd_index][2], 1.0f); - color_data.span[usd_index] = color; + usd_colors[i][0], usd_colors[i][1], usd_colors[i][2], 1.0f); + color_data.span[i] = color; + } + } + + /* Special case: expand uniform color into corner color. + * Uniforms in USD come through as single colors, face-varying. Since Blender does not + * support this particular combination for paintable color attributes, we convert the type + * here to make sure that the user gets the same visual result. + * */ + else if (ELEM(interp, pxr::UsdGeomTokens->uniform)) { + for (int i = 0; i < usd_colors.size(); i++) { + const ColorGeometry4f color = ColorGeometry4f( + usd_colors[i][0], usd_colors[i][1], usd_colors[i][2], 1.0f); + color_data.span[i * 4] = color; + color_data.span[i * 4 + 1] = color; + color_data.span[i * 4 + 2] = color; + color_data.span[i * 4 + 3] = color; + } + } + + else { + /* Loop-level data, but left-handed, requires a bit of a swizzle. */ + const Span polys = mesh->polys(); + + for (const MPoly &poly : polys) { + for (int j = 0; j < poly.totloop; ++j) { + /* Default for constant varying interpolation. */ + int usd_index = poly.loopstart; + if (is_left_handed_) { + usd_index += poly.totloop - 1 - j; + } + else { + usd_index += j; + } + + if (usd_index >= usd_colors.size()) { + continue; + } + + ColorGeometry4f color = ColorGeometry4f( + usd_colors[usd_index][0], usd_colors[usd_index][1], usd_colors[usd_index][2], 1.0f); + color_data.span[usd_index] = color; + } } } } -- 2.30.2 From 9aaf1056fe8cc5105be2970fd83c58e850cabb39 Mon Sep 17 00:00:00 2001 From: Charles Wardlaw Date: Thu, 30 Mar 2023 17:02:54 -0400 Subject: [PATCH 4/5] Addressing last round of comments. --- source/blender/io/usd/intern/usd_reader_mesh.cc | 8 ++++---- source/blender/io/usd/intern/usd_reader_mesh.h | 5 +++-- source/blender/io/usd/intern/usd_writer_mesh.h | 1 - 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index cbb11e82cdd..8cfcb0b12da 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -415,7 +415,7 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo } } -void USDMeshReader::read_color_data_all(Mesh *mesh, const double motionSampleTime) +void USDMeshReader::read_color_data_all_primvars(Mesh *mesh, const double motionSampleTime) { if (!(mesh && mesh_prim_ && mesh->totloop > 0)) { return; @@ -457,7 +457,7 @@ void USDMeshReader::read_color_data_all(Mesh *mesh, const double motionSampleTim continue; } - read_color_data(mesh, pv, motionSampleTime); + read_color_data_primvar(mesh, pv, motionSampleTime); } if (!active_color_name.IsEmpty()) { @@ -465,7 +465,7 @@ void USDMeshReader::read_color_data_all(Mesh *mesh, const double motionSampleTim } } -void USDMeshReader::read_color_data(Mesh *mesh, +void USDMeshReader::read_color_data_primvar(Mesh *mesh, const pxr::UsdGeomPrimvar &color_primvar, const double motionSampleTime) { @@ -765,7 +765,7 @@ void USDMeshReader::read_custom_data(const ImportSettings *settings, const double motionSampleTime) { if ((settings->read_flag & MOD_MESHSEQ_READ_COLOR) != 0) { - read_color_data_all(mesh, motionSampleTime); + read_color_data_all_primvars(mesh, motionSampleTime); } /* TODO: Generic readers for custom data layers not listed above. */ diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h index a50bd097026..7d5c512de1e 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.h +++ b/source/blender/io/usd/intern/usd_reader_mesh.h @@ -77,8 +77,9 @@ class USDMeshReader : public USDGeomReader { Mesh *mesh, double motionSampleTime); - void read_color_data_all(Mesh *mesh, double motionSampleTime); - void read_color_data(Mesh *mesh, const pxr::UsdGeomPrimvar &color_primvar, double motionSampleTime); + void read_color_data_all_primvars(Mesh *mesh, const double motionSampleTime); + void read_color_data_primvar(Mesh *mesh, const pxr::UsdGeomPrimvar &color_primvar, + const double motionSampleTime); }; } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_writer_mesh.h b/source/blender/io/usd/intern/usd_writer_mesh.h index 34aef4e24b0..aab1e09d535 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.h +++ b/source/blender/io/usd/intern/usd_writer_mesh.h @@ -38,7 +38,6 @@ class USDGenericMeshWriter : public USDAbstractWriter { void write_surface_velocity(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); void write_custom_data(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); - void write_color_data(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh, const bke::AttributeIDRef &attribute_id, -- 2.30.2 From 8a882206b723e18e71540fc2806a39bd173a0230 Mon Sep 17 00:00:00 2001 From: Charles Wardlaw Date: Thu, 13 Apr 2023 13:57:14 -0400 Subject: [PATCH 5/5] Adding the requested default color setting on import. --- source/blender/io/usd/intern/usd_reader_mesh.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 0d6cffe6363..762686331f8 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -457,6 +457,7 @@ void USDMeshReader::read_color_data_all_primvars(Mesh *mesh, const double motion } if (!active_color_name.IsEmpty()) { + BKE_id_attributes_default_color_set(&mesh->id, active_color_name.GetText()); BKE_id_attributes_active_color_set(&mesh->id, active_color_name.GetText()); } } -- 2.30.2