Blender V5.0
usd_attribute_utils.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4#pragma once
5
6#include "BLI_color.hh"
10#include "BLI_span.hh"
11#include "BLI_virtual_array.hh"
12
13#include "BKE_attribute.hh"
14
15#include <pxr/base/gf/quatf.h>
16#include <pxr/base/gf/vec2f.h>
17#include <pxr/base/gf/vec3f.h>
18#include <pxr/base/vt/array.h>
19
20#include <pxr/usd/sdf/types.h>
21#include <pxr/usd/sdf/valueTypeName.h>
22#include <pxr/usd/usd/timeCode.h>
23#include <pxr/usd/usdGeom/primvar.h>
24#include <pxr/usd/usdUtils/sparseValueWriter.h>
25
26#include <cstdint>
27#include <optional>
28#include <type_traits>
29
30namespace usdtokens {
31inline const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal);
32}
33
34namespace blender::io::usd {
35
36namespace detail {
37
38/* Until we can use C++20, implement our own version of std::is_layout_compatible.
39 * Types with compatible layouts can be exchanged much more efficiently than otherwise.
40 */
41template<class T, class U> struct is_layout_compatible : std::false_type {};
42
43template<> struct is_layout_compatible<float2, pxr::GfVec2f> : std::true_type {};
44template<> struct is_layout_compatible<float3, pxr::GfVec3f> : std::true_type {};
45
46template<> struct is_layout_compatible<pxr::GfVec2f, float2> : std::true_type {};
47template<> struct is_layout_compatible<pxr::GfVec3f, float3> : std::true_type {};
48
49/* Conversion utilities to convert a Blender type to an USD type. */
50template<typename From, typename To> inline To convert_value(const From value)
51{
52 return value;
53}
54
55template<> inline pxr::GfVec2f convert_value(const float2 value)
56{
57 return pxr::GfVec2f(value[0], value[1]);
58}
59template<> inline pxr::GfVec3f convert_value(const float3 value)
60{
61 return pxr::GfVec3f(value[0], value[1], value[2]);
62}
63template<> inline pxr::GfVec3f convert_value(const ColorGeometry4f value)
64{
65 return pxr::GfVec3f(value.r, value.g, value.b);
66}
67template<> inline pxr::GfVec4f convert_value(const ColorGeometry4f value)
68{
69 return pxr::GfVec4f(value.r, value.g, value.b, value.a);
70}
71template<> inline pxr::GfVec3f convert_value(const ColorGeometry4b value)
72{
73 ColorGeometry4f color4f = color::decode(value);
74 return pxr::GfVec3f(color4f.r, color4f.g, color4f.b);
75}
76template<> inline pxr::GfVec4f convert_value(const ColorGeometry4b value)
77{
78 ColorGeometry4f color4f = color::decode(value);
79 return pxr::GfVec4f(color4f.r, color4f.g, color4f.b, color4f.a);
80}
81template<> inline pxr::GfQuatf convert_value(const math::Quaternion value)
82{
83 return pxr::GfQuatf(value.w, value.x, value.y, value.z);
84}
85
86template<> inline float2 convert_value(const pxr::GfVec2f value)
87{
88 return float2(value[0], value[1]);
89}
90template<> inline float3 convert_value(const pxr::GfVec3f value)
91{
92 return float3(value[0], value[1], value[2]);
93}
94template<> inline ColorGeometry4f convert_value(const pxr::GfVec3f value)
95{
96 return ColorGeometry4f(value[0], value[1], value[2], 1.0f);
97}
98template<> inline ColorGeometry4f convert_value(const pxr::GfVec4f value)
99{
100 return ColorGeometry4f(value[0], value[1], value[2], value[3]);
101}
102template<> inline math::Quaternion convert_value(const pxr::GfQuatf value)
103{
104 const pxr::GfVec3f &img = value.GetImaginary();
105 return math::Quaternion(value.GetReal(), img[0], img[1], img[2]);
106}
107
108template<class T> struct is_vt_array : std::false_type {};
109template<class T> struct is_vt_array<pxr::VtArray<T>> : std::true_type {};
110
111} // namespace detail
112
113std::optional<pxr::SdfValueTypeName> convert_blender_type_to_usd(const bke::AttrType blender_type,
114 bool use_color3f_type = false);
115
116std::optional<bke::AttrType> convert_usd_type_to_blender(const pxr::SdfValueTypeName usd_type);
117
122template<typename USDT>
123void set_attribute(const pxr::UsdAttribute &attr,
124 const USDT value,
125 pxr::UsdTimeCode time,
126 pxr::UsdUtilsSparseValueWriter &value_writer)
127{
128 /* This overload should only be use with non-VtArray types. If it is not, then that indicates
129 * an issue on the caller side, usually because of using a const reference rather than non-const
130 * for the `value` parameter. */
131 static_assert(!detail::is_vt_array<USDT>::value, "Wrong set_attribute overload selected.");
132
133 if (!attr.HasValue()) {
134 attr.Set(value, pxr::UsdTimeCode::Default());
135 }
136
137 value_writer.SetAttribute(attr, pxr::VtValue(value), time);
138}
139
144template<typename USDT>
145void set_attribute(const pxr::UsdAttribute &attr,
146 pxr::VtArray<USDT> &value,
147 pxr::UsdTimeCode time,
148 pxr::UsdUtilsSparseValueWriter &value_writer)
149{
150 if (!attr.HasValue()) {
151 attr.Set(value, pxr::UsdTimeCode::Default());
152 }
153
154 pxr::VtValue val = pxr::VtValue::Take(value);
155 value_writer.SetAttribute(attr, &val, time);
156}
157
158/* Copy a typed Blender attribute array into a typed USD primvar attribute. */
159template<typename BlenderT, typename USDT>
161 const pxr::UsdTimeCode time,
162 const pxr::UsdGeomPrimvar &primvar,
163 pxr::UsdUtilsSparseValueWriter &value_writer)
164{
165 constexpr bool is_same = std::is_same_v<BlenderT, USDT>;
166 constexpr bool is_compatible = detail::is_layout_compatible<BlenderT, USDT>::value;
167
168 pxr::VtArray<USDT> usd_data;
169 if (const std::optional<BlenderT> value = buffer.get_if_single()) {
170 usd_data.assign(buffer.size(), detail::convert_value<BlenderT, USDT>(*value));
171 }
172 else {
173 const VArraySpan<BlenderT> data(buffer);
174 if constexpr (is_same || is_compatible) {
175 usd_data.assign(data.template cast<USDT>().begin(), data.template cast<USDT>().end());
176 }
177 else {
178 usd_data.resize(data.size());
179 for (const int i : data.index_range()) {
181 }
182 }
183 }
184
185 set_attribute(primvar, usd_data, time, value_writer);
186}
187
188void copy_blender_attribute_to_primvar(const GVArray &attribute,
189 const bke::AttrType data_type,
190 const pxr::UsdTimeCode time,
191 const pxr::UsdGeomPrimvar &primvar,
192 pxr::UsdUtilsSparseValueWriter &value_writer);
193
194template<typename T>
195pxr::VtArray<T> get_primvar_array(const pxr::UsdGeomPrimvar &primvar, const pxr::UsdTimeCode time)
196{
197 pxr::VtValue primvar_val;
198 if (!primvar.ComputeFlattened(&primvar_val, time)) {
199 return {};
200 }
201
202 if (!primvar_val.CanCast<pxr::VtArray<T>>()) {
203 return {};
204 }
205
206 return primvar_val.Cast<pxr::VtArray<T>>().template UncheckedGet<pxr::VtArray<T>>();
207}
208
209template<typename USDT, typename BlenderT>
210void copy_primvar_to_blender_buffer(const pxr::UsdGeomPrimvar &primvar,
211 const pxr::UsdTimeCode time,
213 MutableSpan<BlenderT> attribute)
214{
215 const pxr::VtArray<USDT> usd_data = get_primvar_array<USDT>(primvar, time);
216 if (usd_data.empty()) {
217 return;
218 }
219
220 constexpr bool is_same = std::is_same_v<USDT, BlenderT>;
221 constexpr bool is_compatible = detail::is_layout_compatible<USDT, BlenderT>::value;
222
223 const pxr::TfToken pv_interp = primvar.GetInterpolation();
224 if (pv_interp == pxr::UsdGeomTokens->constant) {
225 /* For situations where there's only a single item, flood fill the object. */
226 attribute.fill(detail::convert_value<USDT, BlenderT>(usd_data[0]));
227 }
228 else if (pv_interp == pxr::UsdGeomTokens->faceVarying) {
229 if (!faces.is_empty()) {
230 /* Reverse the index order. */
231 for (const int i : faces.index_range()) {
232 const IndexRange face = faces[i];
233 for (int j : face.index_range()) {
234 const int rev_index = face.last(j);
235 attribute[face.start() + j] = detail::convert_value<USDT, BlenderT>(usd_data[rev_index]);
236 }
237 }
238 }
239 else {
240 if constexpr (is_same || is_compatible) {
241 const Span<USDT> src(usd_data.data(), usd_data.size());
242 attribute.copy_from(src.template cast<BlenderT>());
243 }
244 else {
245 for (const int64_t i : attribute.index_range()) {
246 attribute[i] = detail::convert_value<USDT, BlenderT>(usd_data[i]);
247 }
248 }
249 }
250 }
251 else {
252 /* Assume direct one-to-one mapping. */
253 if (usd_data.size() == attribute.size()) {
254 if constexpr (is_same || is_compatible) {
255 const Span<USDT> src(usd_data.data(), usd_data.size());
256 attribute.copy_from(src.template cast<BlenderT>());
257 }
258 else {
259 for (const int64_t i : attribute.index_range()) {
260 attribute[i] = detail::convert_value<USDT, BlenderT>(usd_data[i]);
261 }
262 }
263 }
264 }
265}
266
267void copy_primvar_to_blender_attribute(const pxr::UsdGeomPrimvar &primvar,
268 const pxr::UsdTimeCode time,
269 const bke::AttrType data_type,
270 const bke::AttrDomain domain,
271 const OffsetIndices<int> face_indices,
273
274} // namespace blender::io::usd
BMesh const char void * data
long long int int64_t
ChannelStorageType r
ChannelStorageType g
ChannelStorageType b
ChannelStorageType a
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t start() const
constexpr IndexRange index_range() const
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
std::optional< T > get_if_single() const
#define cast
static char faces[256]
BLI_INLINE ColorSceneLinear4f< Alpha > decode(const ColorSceneLinearByteEncoded4b< Alpha > &color)
Definition BLI_color.hh:77
To convert_value(const From value)
void copy_blender_attribute_to_primvar(const GVArray &attribute, const bke::AttrType data_type, const pxr::UsdTimeCode time, const pxr::UsdGeomPrimvar &primvar, pxr::UsdUtilsSparseValueWriter &value_writer)
pxr::VtArray< T > get_primvar_array(const pxr::UsdGeomPrimvar &primvar, const pxr::UsdTimeCode time)
std::optional< pxr::SdfValueTypeName > convert_blender_type_to_usd(const bke::AttrType blender_type, bool use_color3f_type)
void copy_blender_buffer_to_primvar(const VArray< BlenderT > &buffer, const pxr::UsdTimeCode time, const pxr::UsdGeomPrimvar &primvar, pxr::UsdUtilsSparseValueWriter &value_writer)
void copy_primvar_to_blender_attribute(const pxr::UsdGeomPrimvar &primvar, const pxr::UsdTimeCode time, const bke::AttrType data_type, const bke::AttrDomain domain, const OffsetIndices< int > face_indices, bke::MutableAttributeAccessor attributes)
void copy_primvar_to_blender_buffer(const pxr::UsdGeomPrimvar &primvar, const pxr::UsdTimeCode time, const OffsetIndices< int > faces, MutableSpan< BlenderT > attribute)
std::optional< bke::AttrType > convert_usd_type_to_blender(const pxr::SdfValueTypeName usd_type)
void set_attribute(const pxr::UsdAttribute &attr, const USDT value, pxr::UsdTimeCode time, pxr::UsdUtilsSparseValueWriter &value_writer)
QuaternionBase< float > Quaternion
VecBase< float, 2 > float2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
VecBase< float, 3 > float3
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal)
i
Definition text_draw.cc:230