USD import: add support for displayColor on non-Mesh USD shape prims
Blender USD import already supports the displayColor primvar, when it is specified on Mesh prims. These colors are visible in the viewport when the viewport shading color type is set to Attribute. This PR extends this displayColor primvar support to also work with the various shape prims, such as Box, Sphere, etc. The original displayColor support was implemented in USDMeshReader. Because USDShapeReader is a sibling to to USDMeshReader, and USDShapeReader first converts shapes to a Mesh, we have factored out the mutually beneficial code from usd_reader_mesh.cc into a new usd_mesh_utils.cc. For now only the displayColor primvar is supported on shapes, but this could be easily extended in a future PR. Authored by Apple: Matt McLin Co-authored-by: Michael Kowalski <makowalski@nvidia.com> Pull Request: https://projects.blender.org/blender/blender/pulls/120236
This commit is contained in:
committed by
Jesse Yurkovich
parent
777473313e
commit
f91b23ef2d
@@ -93,6 +93,7 @@ set(SRC
|
||||
intern/usd_capi_import.cc
|
||||
intern/usd_hierarchy_iterator.cc
|
||||
intern/usd_hook.cc
|
||||
intern/usd_mesh_utils.cc
|
||||
intern/usd_writer_abstract.cc
|
||||
intern/usd_writer_armature.cc
|
||||
intern/usd_writer_camera.cc
|
||||
@@ -134,6 +135,7 @@ set(SRC
|
||||
intern/usd_hash_types.hh
|
||||
intern/usd_hierarchy_iterator.hh
|
||||
intern/usd_hook.hh
|
||||
intern/usd_mesh_utils.hh
|
||||
intern/usd_writer_abstract.hh
|
||||
intern/usd_writer_armature.hh
|
||||
intern/usd_writer_camera.hh
|
||||
|
||||
234
source/blender/io/usd/intern/usd_mesh_utils.cc
Normal file
234
source/blender/io/usd/intern/usd_mesh_utils.cc
Normal file
@@ -0,0 +1,234 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "usd_mesh_utils.hh"
|
||||
#include "usd_hash_types.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
std::optional<eCustomDataType> convert_usd_type_to_blender(const pxr::SdfValueTypeName usd_type,
|
||||
ReportList *reports)
|
||||
{
|
||||
static const blender::Map<pxr::SdfValueTypeName, eCustomDataType> type_map = []() {
|
||||
blender::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->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->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) {
|
||||
BKE_reportf(reports,
|
||||
RPT_WARNING,
|
||||
"Unsupported type %s for mesh data",
|
||||
usd_type.GetAsToken().GetText());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
/* To avoid putting the templated method definition in the header file,
|
||||
* it is necessary to define each of the possible template instantiations
|
||||
* that we support. Ugly here, but it keeps the header looking clean.
|
||||
*/
|
||||
template pxr::VtArray<pxr::GfVec2f> get_prim_attribute_array<pxr::GfVec2f>(
|
||||
const pxr::UsdGeomPrimvar &primvar, const double motionSampleTime, ReportList *reports);
|
||||
template pxr::VtArray<pxr::GfVec3f> get_prim_attribute_array<pxr::GfVec3f>(
|
||||
const pxr::UsdGeomPrimvar &primvar, const double motionSampleTime, ReportList *reports);
|
||||
template pxr::VtArray<bool> get_prim_attribute_array<bool>(const pxr::UsdGeomPrimvar &primvar,
|
||||
const double motionSampleTime,
|
||||
ReportList *reports);
|
||||
template pxr::VtArray<int> get_prim_attribute_array<int>(const pxr::UsdGeomPrimvar &primvar,
|
||||
const double motionSampleTime,
|
||||
ReportList *reports);
|
||||
template pxr::VtArray<float> get_prim_attribute_array<float>(const pxr::UsdGeomPrimvar &primvar,
|
||||
const double motionSampleTime,
|
||||
ReportList *reports);
|
||||
|
||||
template<typename T>
|
||||
pxr::VtArray<T> get_prim_attribute_array(const pxr::UsdGeomPrimvar &primvar,
|
||||
const double motionSampleTime,
|
||||
ReportList *reports)
|
||||
{
|
||||
pxr::VtArray<T> array;
|
||||
|
||||
pxr::VtValue primvar_val;
|
||||
|
||||
if (!primvar.ComputeFlattened(&primvar_val, motionSampleTime)) {
|
||||
BKE_reportf(reports,
|
||||
RPT_WARNING,
|
||||
"USD Import: unable to get array values for primvar '%s'",
|
||||
primvar.GetName().GetText());
|
||||
return array;
|
||||
}
|
||||
|
||||
if (!primvar_val.CanCast<pxr::VtArray<T>>()) {
|
||||
BKE_reportf(reports,
|
||||
RPT_WARNING,
|
||||
"USD Import: can't cast attribute '%s' to array",
|
||||
primvar.GetName().GetText());
|
||||
return array;
|
||||
}
|
||||
|
||||
array = primvar_val.Cast<pxr::VtArray<T>>().template UncheckedGet<pxr::VtArray<T>>();
|
||||
return array;
|
||||
}
|
||||
|
||||
void read_color_data_primvar(Mesh *mesh,
|
||||
const pxr::UsdGeomPrimvar &primvar,
|
||||
double motion_sample_time,
|
||||
ReportList *reports,
|
||||
bool is_left_handed)
|
||||
{
|
||||
if (!(mesh && primvar && primvar.HasValue())) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::VtArray<pxr::GfVec3f> usd_colors = get_prim_attribute_array<pxr::GfVec3f>(
|
||||
primvar, motion_sample_time, reports);
|
||||
|
||||
if (usd_colors.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::TfToken interp = primvar.GetInterpolation();
|
||||
|
||||
if ((interp == pxr::UsdGeomTokens->faceVarying && usd_colors.size() != mesh->corners_num) ||
|
||||
(interp == pxr::UsdGeomTokens->varying && usd_colors.size() != mesh->corners_num) ||
|
||||
(interp == pxr::UsdGeomTokens->vertex && usd_colors.size() != mesh->verts_num) ||
|
||||
(interp == pxr::UsdGeomTokens->constant && usd_colors.size() != 1) ||
|
||||
(interp == pxr::UsdGeomTokens->uniform && usd_colors.size() != mesh->faces_num))
|
||||
{
|
||||
BKE_reportf(
|
||||
reports,
|
||||
RPT_WARNING,
|
||||
"USD Import: color attribute value '%s' count inconsistent with interpolation type",
|
||||
primvar.GetName().GetText());
|
||||
return;
|
||||
}
|
||||
|
||||
const StringRef primvar_name(primvar.GetBaseName().GetString());
|
||||
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
||||
|
||||
bke::AttrDomain color_domain = bke::AttrDomain::Point;
|
||||
|
||||
if (ELEM(interp,
|
||||
pxr::UsdGeomTokens->varying,
|
||||
pxr::UsdGeomTokens->faceVarying,
|
||||
pxr::UsdGeomTokens->uniform))
|
||||
{
|
||||
color_domain = bke::AttrDomain::Corner;
|
||||
}
|
||||
|
||||
bke::SpanAttributeWriter<ColorGeometry4f> color_data;
|
||||
color_data = attributes.lookup_or_add_for_write_only_span<ColorGeometry4f>(primvar_name,
|
||||
color_domain);
|
||||
if (!color_data) {
|
||||
BKE_reportf(reports,
|
||||
RPT_WARNING,
|
||||
"USD Import: couldn't add color attribute '%s'",
|
||||
primvar.GetBaseName().GetText());
|
||||
return;
|
||||
}
|
||||
|
||||
if (ELEM(interp, pxr::UsdGeomTokens->constant)) {
|
||||
/* 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));
|
||||
}
|
||||
/* Check for situations that allow for a straight-forward copy by index. */
|
||||
else if (interp == pxr::UsdGeomTokens->vertex ||
|
||||
(interp == pxr::UsdGeomTokens->faceVarying && !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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Catch all for the remaining cases. */
|
||||
|
||||
/* Special case: we will 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.
|
||||
*/
|
||||
const OffsetIndices faces = mesh->faces();
|
||||
const Span<int> corner_verts = mesh->corner_verts();
|
||||
for (const int i : faces.index_range()) {
|
||||
const IndexRange face = faces[i];
|
||||
for (int j = 0; j < face.size(); ++j) {
|
||||
int loop_index = face[j];
|
||||
|
||||
/* Default for constant interpolation. */
|
||||
int usd_index = 0;
|
||||
|
||||
if (interp == pxr::UsdGeomTokens->vertex) {
|
||||
usd_index = corner_verts[loop_index];
|
||||
}
|
||||
else if (interp == pxr::UsdGeomTokens->faceVarying) {
|
||||
usd_index = face.start();
|
||||
if (is_left_handed) {
|
||||
usd_index += face.size() - 1 - j;
|
||||
}
|
||||
else {
|
||||
usd_index += j;
|
||||
}
|
||||
}
|
||||
else if (interp == pxr::UsdGeomTokens->uniform) {
|
||||
/* Uniform varying uses the face index. */
|
||||
usd_index = i;
|
||||
}
|
||||
|
||||
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[loop_index] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color_data.finish();
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
||||
37
source/blender/io/usd/intern/usd_mesh_utils.hh
Normal file
37
source/blender/io/usd/intern/usd_mesh_utils.hh
Normal file
@@ -0,0 +1,37 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
#include "usd.hh"
|
||||
|
||||
#include <pxr/base/vt/array.h>
|
||||
#include <pxr/usd/sdf/valueTypeName.h>
|
||||
#include <pxr/usd/usdGeom/primvar.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
struct Mesh;
|
||||
struct ReportList;
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
std::optional<eCustomDataType> convert_usd_type_to_blender(const pxr::SdfValueTypeName usd_type,
|
||||
ReportList *reports);
|
||||
|
||||
template<typename T>
|
||||
pxr::VtArray<T> get_prim_attribute_array(const pxr::UsdGeomPrimvar &primvar,
|
||||
const double motionSampleTime,
|
||||
ReportList *reports);
|
||||
|
||||
void read_color_data_primvar(Mesh *mesh,
|
||||
const pxr::UsdGeomPrimvar &color_primvar,
|
||||
double motion_sample_time,
|
||||
ReportList *reports,
|
||||
bool is_left_handed);
|
||||
|
||||
} // namespace blender::io::usd
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "usd_reader_mesh.hh"
|
||||
#include "usd_hash_types.hh"
|
||||
#include "usd_mesh_utils.hh"
|
||||
#include "usd_reader_material.hh"
|
||||
#include "usd_skel_convert.hh"
|
||||
|
||||
@@ -158,54 +159,6 @@ USDMeshReader::USDMeshReader(const pxr::UsdPrim &prim,
|
||||
{
|
||||
}
|
||||
|
||||
static std::optional<eCustomDataType> convert_usd_type_to_blender(
|
||||
const pxr::SdfValueTypeName usd_type, ReportList *reports)
|
||||
{
|
||||
static const blender::Map<pxr::SdfValueTypeName, eCustomDataType> type_map = []() {
|
||||
blender::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->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->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) {
|
||||
BKE_reportf(reports,
|
||||
RPT_WARNING,
|
||||
"Unsupported type %s for mesh data",
|
||||
usd_type.GetAsToken().GetText());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
static const std::optional<bke::AttrDomain> convert_usd_varying_to_blender(
|
||||
const pxr::TfToken usd_domain, ReportList *reports)
|
||||
{
|
||||
@@ -353,150 +306,6 @@ void USDMeshReader::read_mpolys(Mesh *mesh)
|
||||
bke::mesh_calc_edges(*mesh, false, false);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
pxr::VtArray<T> get_prim_attribute_array(const pxr::UsdGeomPrimvar &primvar,
|
||||
const double motionSampleTime,
|
||||
ReportList *reports)
|
||||
{
|
||||
pxr::VtArray<T> array;
|
||||
|
||||
pxr::VtValue primvar_val;
|
||||
|
||||
if (!primvar.ComputeFlattened(&primvar_val, motionSampleTime)) {
|
||||
BKE_reportf(reports,
|
||||
RPT_WARNING,
|
||||
"Unable to get array values for primvar %s",
|
||||
primvar.GetName().GetText());
|
||||
return array;
|
||||
}
|
||||
|
||||
if (!primvar_val.CanCast<pxr::VtArray<T>>()) {
|
||||
BKE_reportf(reports,
|
||||
RPT_WARNING,
|
||||
"USD Import: can't cast attribute '%s' to array",
|
||||
primvar.GetName().GetText());
|
||||
return array;
|
||||
}
|
||||
|
||||
array = primvar_val.Cast<pxr::VtArray<T>>().template UncheckedGet<pxr::VtArray<T>>();
|
||||
return array;
|
||||
}
|
||||
|
||||
void USDMeshReader::read_color_data_primvar(Mesh *mesh,
|
||||
const pxr::UsdGeomPrimvar &primvar,
|
||||
const double motionSampleTime)
|
||||
{
|
||||
if (!(mesh && primvar && primvar.HasValue())) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::VtArray<pxr::GfVec3f> usd_colors = get_prim_attribute_array<pxr::GfVec3f>(
|
||||
primvar, motionSampleTime, reports());
|
||||
|
||||
if (usd_colors.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::TfToken interp = primvar.GetInterpolation();
|
||||
|
||||
if ((interp == pxr::UsdGeomTokens->faceVarying && usd_colors.size() != mesh->corners_num) ||
|
||||
(interp == pxr::UsdGeomTokens->varying && usd_colors.size() != mesh->verts_num) ||
|
||||
(interp == pxr::UsdGeomTokens->vertex && usd_colors.size() != mesh->verts_num) ||
|
||||
(interp == pxr::UsdGeomTokens->constant && usd_colors.size() != 1) ||
|
||||
(interp == pxr::UsdGeomTokens->uniform && usd_colors.size() != mesh->faces_num))
|
||||
{
|
||||
BKE_reportf(
|
||||
reports(),
|
||||
RPT_WARNING,
|
||||
"USD Import: color attribute value '%s' count inconsistent with interpolation type",
|
||||
primvar.GetName().GetText());
|
||||
return;
|
||||
}
|
||||
|
||||
const StringRef primvar_name(primvar.GetBaseName().GetString());
|
||||
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
|
||||
|
||||
bke::AttrDomain color_domain = bke::AttrDomain::Point;
|
||||
|
||||
if (ELEM(interp, pxr::UsdGeomTokens->faceVarying, pxr::UsdGeomTokens->uniform)) {
|
||||
color_domain = bke::AttrDomain::Corner;
|
||||
}
|
||||
|
||||
bke::SpanAttributeWriter<ColorGeometry4f> color_data;
|
||||
color_data = attributes.lookup_or_add_for_write_only_span<ColorGeometry4f>(primvar_name,
|
||||
color_domain);
|
||||
if (!color_data) {
|
||||
BKE_reportf(reports(),
|
||||
RPT_WARNING,
|
||||
"USD Import: couldn't add color attribute '%s'",
|
||||
primvar.GetBaseName().GetText());
|
||||
return;
|
||||
}
|
||||
|
||||
if (ELEM(interp, pxr::UsdGeomTokens->constant)) {
|
||||
/* 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));
|
||||
}
|
||||
/* Check for situations that allow for a straight-forward copy by index. */
|
||||
else if (interp == pxr::UsdGeomTokens->vertex || interp == pxr::UsdGeomTokens->varying ||
|
||||
(interp == pxr::UsdGeomTokens->faceVarying && !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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Catch all for the remaining cases. */
|
||||
|
||||
/* Special case: we will 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.
|
||||
*/
|
||||
const OffsetIndices faces = mesh->faces();
|
||||
const Span<int> corner_verts = mesh->corner_verts();
|
||||
for (const int i : faces.index_range()) {
|
||||
const IndexRange face = faces[i];
|
||||
for (int j = 0; j < face.size(); ++j) {
|
||||
int loop_index = face[j];
|
||||
|
||||
/* Default for constant interpolation. */
|
||||
int usd_index = 0;
|
||||
|
||||
if (interp == pxr::UsdGeomTokens->vertex) {
|
||||
usd_index = corner_verts[loop_index];
|
||||
}
|
||||
else if (interp == pxr::UsdGeomTokens->faceVarying) {
|
||||
usd_index = face.start();
|
||||
if (is_left_handed_) {
|
||||
usd_index += face.size() - 1 - j;
|
||||
}
|
||||
else {
|
||||
usd_index += j;
|
||||
}
|
||||
}
|
||||
else if (interp == pxr::UsdGeomTokens->uniform) {
|
||||
/* Uniform varying uses the face index. */
|
||||
usd_index = i;
|
||||
}
|
||||
|
||||
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[loop_index] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color_data.finish();
|
||||
}
|
||||
|
||||
void USDMeshReader::read_uv_data_primvar(Mesh *mesh,
|
||||
const pxr::UsdGeomPrimvar &primvar,
|
||||
const double motionSampleTime)
|
||||
@@ -956,7 +765,7 @@ void USDMeshReader::read_custom_data(const ImportSettings *settings,
|
||||
active_color_name = name;
|
||||
}
|
||||
|
||||
read_color_data_primvar(mesh, pv, motionSampleTime);
|
||||
read_color_data_primvar(mesh, pv, motionSampleTime, reports(), is_left_handed_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,10 +94,6 @@ class USDMeshReader : public USDGeomReader {
|
||||
double motionSampleTime,
|
||||
bool new_mesh);
|
||||
|
||||
void read_color_data_primvar(Mesh *mesh,
|
||||
const pxr::UsdGeomPrimvar &color_primvar,
|
||||
const double motionSampleTime);
|
||||
|
||||
void read_uv_data_primvar(Mesh *mesh,
|
||||
const pxr::UsdGeomPrimvar &primvar,
|
||||
const double motionSampleTime);
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "usd_hash_types.hh"
|
||||
#include "usd_mesh_utils.hh"
|
||||
#include "usd_reader_shape.hh"
|
||||
|
||||
#include <pxr/usd/usdGeom/capsule.h>
|
||||
@@ -24,6 +28,11 @@
|
||||
#include <pxr/usdImaging/usdImaging/cylinderAdapter.h>
|
||||
#include <pxr/usdImaging/usdImaging/sphereAdapter.h>
|
||||
|
||||
namespace usdtokens {
|
||||
/* Materials */
|
||||
static const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal);
|
||||
} // namespace usdtokens
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
USDShapeReader::USDShapeReader(const pxr::UsdPrim &prim,
|
||||
@@ -134,8 +143,8 @@ Mesh *USDShapeReader::read_mesh(Mesh *existing_mesh,
|
||||
}
|
||||
|
||||
/* Should have a good set of data by this point-- copy over. */
|
||||
Mesh *active_mesh = mesh_from_prim(
|
||||
existing_mesh, params.motion_sample_time, face_indices, face_counts);
|
||||
Mesh *active_mesh = mesh_from_prim(existing_mesh, params, face_indices, face_counts);
|
||||
|
||||
if (active_mesh == existing_mesh) {
|
||||
return existing_mesh;
|
||||
}
|
||||
@@ -170,14 +179,74 @@ void USDShapeReader::read_geometry(bke::GeometrySet &geometry_set,
|
||||
}
|
||||
}
|
||||
|
||||
void USDShapeReader::apply_primvars_to_mesh(Mesh *mesh, const double motionSampleTime) const
|
||||
{
|
||||
/* TODO: also handle the displayOpacity primvar. */
|
||||
if (!mesh || !prim_) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(prim_);
|
||||
std::vector<pxr::UsdGeomPrimvar> primvars = pv_api.GetPrimvarsWithValues();
|
||||
|
||||
pxr::TfToken active_color_name;
|
||||
|
||||
for (pxr::UsdGeomPrimvar &pv : primvars) {
|
||||
if (!pv.HasValue()) {
|
||||
BKE_reportf(reports(),
|
||||
RPT_WARNING,
|
||||
"Skipping primvar %s, mesh %s -- no value",
|
||||
pv.GetName().GetText(),
|
||||
&mesh->id.name[2]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pv.GetAttr().GetTypeName().IsArray()) {
|
||||
/* Non-array attributes are technically improper USD. */
|
||||
continue;
|
||||
}
|
||||
|
||||
const pxr::TfToken name = pv.StripPrimvarsName(pv.GetPrimvarName());
|
||||
|
||||
/* Skip reading primvars that have been read before and are not time varying. */
|
||||
if (primvar_time_varying_map_.contains(name) && !primvar_time_varying_map_.lookup(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pxr::SdfValueTypeName sdf_type = pv.GetTypeName();
|
||||
|
||||
const std::optional<eCustomDataType> type = convert_usd_type_to_blender(sdf_type, reports());
|
||||
if (type == CD_PROP_COLOR) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
read_color_data_primvar(mesh, pv, motionSampleTime, reports(), false);
|
||||
|
||||
/* Record whether the primvar attribute might be time varying. */
|
||||
if (!primvar_time_varying_map_.contains(name)) {
|
||||
primvar_time_varying_map_.add(name, pv.ValueMightBeTimeVarying());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Mesh *USDShapeReader::mesh_from_prim(Mesh *existing_mesh,
|
||||
double motionSampleTime,
|
||||
const USDMeshReadParams params,
|
||||
pxr::VtIntArray &face_indices,
|
||||
pxr::VtIntArray &face_counts) const
|
||||
{
|
||||
pxr::VtVec3fArray positions;
|
||||
|
||||
if (!read_mesh_values(motionSampleTime, positions, face_indices, face_counts)) {
|
||||
if (!read_mesh_values(params.motion_sample_time, positions, face_indices, face_counts)) {
|
||||
return existing_mesh;
|
||||
}
|
||||
|
||||
@@ -203,11 +272,25 @@ Mesh *USDShapeReader::mesh_from_prim(Mesh *existing_mesh,
|
||||
vert_positions[i][2] = positions[i][2];
|
||||
}
|
||||
|
||||
if (params.read_flags & MOD_MESHSEQ_READ_COLOR) {
|
||||
if (active_mesh != existing_mesh) {
|
||||
/* Clear the primvar map to force attributes to be reloaded. */
|
||||
this->primvar_time_varying_map_.clear();
|
||||
}
|
||||
apply_primvars_to_mesh(active_mesh, params.motion_sample_time);
|
||||
}
|
||||
|
||||
return active_mesh;
|
||||
}
|
||||
|
||||
bool USDShapeReader::is_time_varying()
|
||||
{
|
||||
for (const bool animating_flag : primvar_time_varying_map_.values()) {
|
||||
if (animating_flag) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (prim_.IsA<pxr::UsdGeomCapsule>()) {
|
||||
pxr::UsdGeomCapsule geom(prim_);
|
||||
return (geom.GetAxisAttr().ValueMightBeTimeVarying() ||
|
||||
|
||||
@@ -16,6 +16,11 @@ namespace blender::io::usd {
|
||||
* as the GL viewport to generate geometry for each of the supported types.
|
||||
*/
|
||||
class USDShapeReader : public USDGeomReader {
|
||||
/* A cache to record whether a given primvar is time-varying, so that static primvars are not
|
||||
* read more than once when the mesh is evaluated for animation by the cache file modifier.
|
||||
* The map is mutable so that it can be updated in const functions. */
|
||||
mutable blender::Map<const pxr::TfToken, bool> primvar_time_varying_map_;
|
||||
|
||||
private:
|
||||
/* Template required to read mesh information out of Shape prims,
|
||||
* as each prim type has a separate subclass. */
|
||||
@@ -32,10 +37,12 @@ class USDShapeReader : public USDGeomReader {
|
||||
pxr::VtIntArray &face_indices,
|
||||
pxr::VtIntArray &face_counts) const;
|
||||
|
||||
void apply_primvars_to_mesh(Mesh *mesh, double motionSampleTime) const;
|
||||
|
||||
/* Read the pxr:UsdGeomMesh values and convert them to a Blender Mesh,
|
||||
* also returning face_indices and counts for further loop processing. */
|
||||
Mesh *mesh_from_prim(Mesh *existing_mesh,
|
||||
double motionSampleTime,
|
||||
USDMeshReadParams params,
|
||||
pxr::VtIntArray &face_indices,
|
||||
pxr::VtIntArray &face_counts) const;
|
||||
|
||||
@@ -52,6 +59,8 @@ class USDShapeReader : public USDGeomReader {
|
||||
USDMeshReadParams /*params*/,
|
||||
const char ** /*err_str*/) override;
|
||||
|
||||
/* Returns the generated mesh might be affected by time-varying attributes.
|
||||
* This assumes mesh_from_prim() has been called. */
|
||||
bool is_time_varying();
|
||||
|
||||
virtual bool topology_changed(const Mesh * /*existing_mesh*/,
|
||||
|
||||
Reference in New Issue
Block a user