Color primvars/attributes were historically treated as a special case for both import and export. This was mostly done to align with how painting and viewport display works in Blender. Export would generally ignore color attributes except when they were found on a Mesh's Point or FaceCorner domains. And import went out of its way to map incoming color primvars to the FaceCorner domain in more situations than necessary. To facilitate better roundtripping in Blender<=>USD workflows, and to reduce code duplication, this PR teaches the common attribute utilities how to handle color types. The color attributes will now work on all relevant Mesh and Curve domains. There were tests in place for this already but they were set to verify the inverse state, i.e. the technically broken state, until this could be fixed. There remains one special case: "displayColor" primvars and attributes. The "displayColor" is a special primvar in USD and is the de-facto way to set a simple viewport color in that ecosystem. It must also be a color3f type. In order to not regress import, if a "displayColor" primvar is found on the Face domain we will map it to FaceCorner instead so it can be displayed in the viewport; which has been the case for the past several releases. We can drop this special-case if/when Blender can display Face colors through the Viewport Shading "Attribute" color type. Additionally, Blender will export this, and only this, color attribute as a color3f. Note: As was the case prior to this PR, the following 2 discrepancies still prevent "perfect" round-trips: - USD does not have an equivalent to Blender's byte colors; they are treated as float during IO - Blender does not have an equivalent to USD's color3 types; they are treated as color4 during IO Pull Request: https://projects.blender.org/blender/blender/pulls/127784
224 lines
8.9 KiB
C++
224 lines
8.9 KiB
C++
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "usd_attribute_utils.hh"
|
|
#include "usd_hash_types.hh"
|
|
|
|
#include "BLI_generic_span.hh"
|
|
#include "BLI_map.hh"
|
|
#include "BLI_offset_indices.hh"
|
|
#include "BLI_sys_types.h"
|
|
|
|
#include "BKE_attribute.hh"
|
|
|
|
#include "DNA_customdata_types.h"
|
|
|
|
#include <pxr/usd/sdf/valueTypeName.h>
|
|
|
|
#include <optional>
|
|
|
|
namespace blender::io::usd {
|
|
|
|
std::optional<pxr::SdfValueTypeName> convert_blender_type_to_usd(
|
|
const eCustomDataType blender_type, bool use_color3f_type)
|
|
{
|
|
switch (blender_type) {
|
|
case CD_PROP_FLOAT:
|
|
return pxr::SdfValueTypeNames->FloatArray;
|
|
case CD_PROP_INT8:
|
|
return pxr::SdfValueTypeNames->UCharArray;
|
|
case CD_PROP_INT32:
|
|
return pxr::SdfValueTypeNames->IntArray;
|
|
case CD_PROP_FLOAT2:
|
|
return pxr::SdfValueTypeNames->Float2Array;
|
|
case CD_PROP_FLOAT3:
|
|
return pxr::SdfValueTypeNames->Float3Array;
|
|
case CD_PROP_STRING:
|
|
return pxr::SdfValueTypeNames->StringArray;
|
|
case CD_PROP_BOOL:
|
|
return pxr::SdfValueTypeNames->BoolArray;
|
|
case CD_PROP_COLOR:
|
|
case CD_PROP_BYTE_COLOR:
|
|
return use_color3f_type ? pxr::SdfValueTypeNames->Color3fArray :
|
|
pxr::SdfValueTypeNames->Color4fArray;
|
|
case CD_PROP_QUATERNION:
|
|
return pxr::SdfValueTypeNames->QuatfArray;
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::optional<eCustomDataType> convert_usd_type_to_blender(const pxr::SdfValueTypeName usd_type)
|
|
{
|
|
static const Map<pxr::SdfValueTypeName, eCustomDataType> type_map = []() {
|
|
Map<pxr::SdfValueTypeName, eCustomDataType> map;
|
|
map.add_new(pxr::SdfValueTypeNames->FloatArray, CD_PROP_FLOAT);
|
|
map.add_new(pxr::SdfValueTypeNames->Double, CD_PROP_FLOAT);
|
|
map.add_new(pxr::SdfValueTypeNames->UCharArray, CD_PROP_INT8);
|
|
map.add_new(pxr::SdfValueTypeNames->IntArray, CD_PROP_INT32);
|
|
map.add_new(pxr::SdfValueTypeNames->Float2Array, CD_PROP_FLOAT2);
|
|
map.add_new(pxr::SdfValueTypeNames->TexCoord2dArray, CD_PROP_FLOAT2);
|
|
map.add_new(pxr::SdfValueTypeNames->TexCoord2fArray, CD_PROP_FLOAT2);
|
|
map.add_new(pxr::SdfValueTypeNames->TexCoord2hArray, CD_PROP_FLOAT2);
|
|
map.add_new(pxr::SdfValueTypeNames->TexCoord3dArray, CD_PROP_FLOAT2);
|
|
map.add_new(pxr::SdfValueTypeNames->TexCoord3fArray, CD_PROP_FLOAT2);
|
|
map.add_new(pxr::SdfValueTypeNames->TexCoord3hArray, CD_PROP_FLOAT2);
|
|
map.add_new(pxr::SdfValueTypeNames->Float3Array, CD_PROP_FLOAT3);
|
|
map.add_new(pxr::SdfValueTypeNames->Point3fArray, CD_PROP_FLOAT3);
|
|
map.add_new(pxr::SdfValueTypeNames->Point3dArray, CD_PROP_FLOAT3);
|
|
map.add_new(pxr::SdfValueTypeNames->Point3hArray, CD_PROP_FLOAT3);
|
|
map.add_new(pxr::SdfValueTypeNames->Normal3fArray, CD_PROP_FLOAT3);
|
|
map.add_new(pxr::SdfValueTypeNames->Normal3dArray, CD_PROP_FLOAT3);
|
|
map.add_new(pxr::SdfValueTypeNames->Normal3hArray, CD_PROP_FLOAT3);
|
|
map.add_new(pxr::SdfValueTypeNames->Vector3fArray, CD_PROP_FLOAT3);
|
|
map.add_new(pxr::SdfValueTypeNames->Vector3hArray, CD_PROP_FLOAT3);
|
|
map.add_new(pxr::SdfValueTypeNames->Vector3dArray, CD_PROP_FLOAT3);
|
|
map.add_new(pxr::SdfValueTypeNames->Color3fArray, CD_PROP_COLOR);
|
|
map.add_new(pxr::SdfValueTypeNames->Color3hArray, CD_PROP_COLOR);
|
|
map.add_new(pxr::SdfValueTypeNames->Color3dArray, CD_PROP_COLOR);
|
|
map.add_new(pxr::SdfValueTypeNames->Color4fArray, CD_PROP_COLOR);
|
|
map.add_new(pxr::SdfValueTypeNames->Color4hArray, CD_PROP_COLOR);
|
|
map.add_new(pxr::SdfValueTypeNames->Color4dArray, CD_PROP_COLOR);
|
|
map.add_new(pxr::SdfValueTypeNames->StringArray, CD_PROP_STRING);
|
|
map.add_new(pxr::SdfValueTypeNames->BoolArray, CD_PROP_BOOL);
|
|
map.add_new(pxr::SdfValueTypeNames->QuatfArray, CD_PROP_QUATERNION);
|
|
map.add_new(pxr::SdfValueTypeNames->QuatdArray, CD_PROP_QUATERNION);
|
|
map.add_new(pxr::SdfValueTypeNames->QuathArray, CD_PROP_QUATERNION);
|
|
return map;
|
|
}();
|
|
|
|
const eCustomDataType *value = type_map.lookup_ptr(usd_type);
|
|
if (value == nullptr) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
return *value;
|
|
}
|
|
|
|
void copy_primvar_to_blender_attribute(const pxr::UsdGeomPrimvar &primvar,
|
|
const pxr::UsdTimeCode timecode,
|
|
const eCustomDataType data_type,
|
|
const bke::AttrDomain domain,
|
|
const OffsetIndices<int> face_indices,
|
|
bke::MutableAttributeAccessor attributes)
|
|
{
|
|
const pxr::TfToken pv_name = pxr::UsdGeomPrimvar::StripPrimvarsName(primvar.GetPrimvarName());
|
|
|
|
bke::GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
|
|
pv_name.GetText(), domain, data_type);
|
|
|
|
switch (data_type) {
|
|
case CD_PROP_FLOAT:
|
|
copy_primvar_to_blender_buffer<float>(
|
|
primvar, timecode, face_indices, attribute.span.typed<float>());
|
|
break;
|
|
case CD_PROP_INT8:
|
|
copy_primvar_to_blender_buffer<uchar>(
|
|
primvar, timecode, face_indices, attribute.span.typed<int8_t>());
|
|
break;
|
|
case CD_PROP_INT32:
|
|
copy_primvar_to_blender_buffer<int32_t>(
|
|
primvar, timecode, face_indices, attribute.span.typed<int>());
|
|
break;
|
|
case CD_PROP_FLOAT2:
|
|
copy_primvar_to_blender_buffer<pxr::GfVec2f>(
|
|
primvar, timecode, face_indices, attribute.span.typed<float2>());
|
|
break;
|
|
case CD_PROP_FLOAT3:
|
|
copy_primvar_to_blender_buffer<pxr::GfVec3f>(
|
|
primvar, timecode, face_indices, attribute.span.typed<float3>());
|
|
break;
|
|
case CD_PROP_COLOR: {
|
|
const pxr::SdfValueTypeName pv_type = primvar.GetTypeName();
|
|
if (ELEM(pv_type,
|
|
pxr::SdfValueTypeNames->Color3fArray,
|
|
pxr::SdfValueTypeNames->Color3hArray,
|
|
pxr::SdfValueTypeNames->Color3dArray))
|
|
{
|
|
copy_primvar_to_blender_buffer<pxr::GfVec3f>(
|
|
primvar, timecode, face_indices, attribute.span.typed<ColorGeometry4f>());
|
|
}
|
|
else {
|
|
copy_primvar_to_blender_buffer<pxr::GfVec4f>(
|
|
primvar, timecode, face_indices, attribute.span.typed<ColorGeometry4f>());
|
|
}
|
|
} break;
|
|
case CD_PROP_BOOL:
|
|
copy_primvar_to_blender_buffer<bool>(
|
|
primvar, timecode, face_indices, attribute.span.typed<bool>());
|
|
break;
|
|
case CD_PROP_QUATERNION:
|
|
copy_primvar_to_blender_buffer<pxr::GfQuatf>(
|
|
primvar, timecode, face_indices, attribute.span.typed<math::Quaternion>());
|
|
break;
|
|
|
|
default:
|
|
BLI_assert_unreachable();
|
|
}
|
|
|
|
attribute.finish();
|
|
}
|
|
|
|
void copy_blender_attribute_to_primvar(const GVArray &attribute,
|
|
const eCustomDataType data_type,
|
|
const pxr::UsdTimeCode timecode,
|
|
const pxr::UsdGeomPrimvar &primvar,
|
|
pxr::UsdUtilsSparseValueWriter &value_writer)
|
|
{
|
|
switch (data_type) {
|
|
case CD_PROP_FLOAT:
|
|
copy_blender_buffer_to_primvar<float, float>(
|
|
attribute.typed<float>(), timecode, primvar, value_writer);
|
|
break;
|
|
case CD_PROP_INT8:
|
|
copy_blender_buffer_to_primvar<int8_t, uchar>(
|
|
attribute.typed<int8_t>(), timecode, primvar, value_writer);
|
|
break;
|
|
case CD_PROP_INT32:
|
|
copy_blender_buffer_to_primvar<int, int32_t>(
|
|
attribute.typed<int>(), timecode, primvar, value_writer);
|
|
break;
|
|
case CD_PROP_FLOAT2:
|
|
copy_blender_buffer_to_primvar<float2, pxr::GfVec2f>(
|
|
attribute.typed<float2>(), timecode, primvar, value_writer);
|
|
break;
|
|
case CD_PROP_FLOAT3:
|
|
copy_blender_buffer_to_primvar<float3, pxr::GfVec3f>(
|
|
attribute.typed<float3>(), timecode, primvar, value_writer);
|
|
break;
|
|
case CD_PROP_BOOL:
|
|
copy_blender_buffer_to_primvar<bool, bool>(
|
|
attribute.typed<bool>(), timecode, primvar, value_writer);
|
|
break;
|
|
case CD_PROP_COLOR:
|
|
if (primvar.GetTypeName() == pxr::SdfValueTypeNames->Color3fArray) {
|
|
copy_blender_buffer_to_primvar<ColorGeometry4f, pxr::GfVec3f>(
|
|
attribute.typed<ColorGeometry4f>(), timecode, primvar, value_writer);
|
|
}
|
|
else {
|
|
copy_blender_buffer_to_primvar<ColorGeometry4f, pxr::GfVec4f>(
|
|
attribute.typed<ColorGeometry4f>(), timecode, primvar, value_writer);
|
|
}
|
|
break;
|
|
case CD_PROP_BYTE_COLOR:
|
|
if (primvar.GetTypeName() == pxr::SdfValueTypeNames->Color3fArray) {
|
|
copy_blender_buffer_to_primvar<ColorGeometry4b, pxr::GfVec3f>(
|
|
attribute.typed<ColorGeometry4b>(), timecode, primvar, value_writer);
|
|
}
|
|
else {
|
|
copy_blender_buffer_to_primvar<ColorGeometry4b, pxr::GfVec4f>(
|
|
attribute.typed<ColorGeometry4b>(), timecode, primvar, value_writer);
|
|
}
|
|
break;
|
|
case CD_PROP_QUATERNION:
|
|
copy_blender_buffer_to_primvar<math::Quaternion, pxr::GfQuatf>(
|
|
attribute.typed<math::Quaternion>(), timecode, primvar, value_writer);
|
|
break;
|
|
default:
|
|
BLI_assert_unreachable();
|
|
}
|
|
}
|
|
|
|
} // namespace blender::io::usd
|