diff --git a/release/datafiles/locale b/release/datafiles/locale index 4331c8e76c2..547a54b294f 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 4331c8e76c2f42b9fd903716c333d6cdeaa5cebd +Subproject commit 547a54b294f32ee11bce73273c6f183d8b235f92 diff --git a/release/scripts/addons b/release/scripts/addons index b3f0ffc587d..78b0fc30b6b 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit b3f0ffc587d197b37eac9a1566d1d24b7bee7d9a +Subproject commit 78b0fc30b6b6e610ef897fc7d26e812da348f2ff diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 14ab9273409..65ff08e325d 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 14ab9273409ea0231d08ba6e86fdc73d4e459e99 +Subproject commit 65ff08e325d54a58b47fb3219ec7dbf417f20f18 diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index e6426732584..0aff49e3a77 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", @@ -608,7 +608,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 fd4b80b9137..f9fb4b6b92f 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 @@ -410,100 +414,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) @@ -674,9 +732,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 181fd5ebf79..17a8772ed53 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.h +++ b/source/blender/io/usd/intern/usd_reader_mesh.h @@ -67,13 +67,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 62656c902d0..e37856ae774 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -13,7 +13,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" @@ -31,6 +30,8 @@ #include "DNA_object_fluidsim_types.h" #include "DNA_particle_types.h" +#include "WM_api.h" + #include namespace blender::io::usd { @@ -72,6 +73,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); @@ -232,6 +299,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 { diff --git a/source/tools b/source/tools index e133fc08cd3..979bfe2504e 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit e133fc08cd3254bb3d3bd1345028c8486700bca4 +Subproject commit 979bfe2504eb2f446639ab5768aac9b80b1f4864