USD IO: Move to the new Mesh attributes API for colors #104542

Closed
Charles Wardlaw wants to merge 1 commits from CharlesWardlaw/blender:D16103-mesh-color-attributes into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
9 changed files with 218 additions and 65 deletions

@ -1 +1 @@
Subproject commit 4331c8e76c2f42b9fd903716c333d6cdeaa5cebd
Subproject commit 547a54b294f32ee11bce73273c6f183d8b235f92

@ -1 +1 @@
Subproject commit b3f0ffc587d197b37eac9a1566d1d24b7bee7d9a
Subproject commit 78b0fc30b6b6e610ef897fc7d26e812da348f2ff

@ -1 +1 @@
Subproject commit 14ab9273409ea0231d08ba6e86fdc73d4e459e99
Subproject commit 65ff08e325d54a58b47fb3219ec7dbf417f20f18

View File

@ -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",

View File

@ -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 <pxr/usd/usdGeom/mesh.h>
#include <pxr/usd/usdGeom/primvarsAPI.h>
#include <pxr/usd/usdGeom/subset.h>
#include <pxr/usd/usdGeom/primvarsAPI.h>
#include <pxr/usd/usdShade/materialBindingAPI.h>
#include <iostream>
@ -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<pxr::UsdGeomPrimvar> 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 pxr::TfToken, bool>::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<pxr::GfVec3f> display_colors;
pxr::VtArray<pxr::GfVec3f> 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<ColorGeometry4f> color_data;
color_data = attributes.lookup_or_add_for_write_only_span<ColorGeometry4f>(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<MLoopCol *>(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<MPoly> polys = mesh->polys();
const Span<MLoop> 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<MPoly> 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,

View File

@ -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

View File

@ -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 <iostream>
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<ColorGeometry4f> attribute = mesh->attributes().lookup_or_default<ColorGeometry4f>(
attribute_id, meta_data.domain, {0.0f, 0.0f, 0.0f, 1.0f});
pxr::VtArray<pxr::GfVec3f> 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);
}

View File

@ -4,6 +4,8 @@
#include "usd_writer_abstract.h"
#include "BKE_attribute.hh"
#include <pxr/usd/usdGeom/mesh.h>
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 {

@ -1 +1 @@
Subproject commit e133fc08cd3254bb3d3bd1345028c8486700bca4
Subproject commit 979bfe2504eb2f446639ab5768aac9b80b1f4864