Blender V4.3
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
16
17#include <pxr/base/gf/quatf.h>
18#include <pxr/base/gf/vec2f.h>
19#include <pxr/base/gf/vec3f.h>
20#include <pxr/base/vt/array.h>
21
22#include <pxr/usd/sdf/types.h>
23#include <pxr/usd/sdf/valueTypeName.h>
24#include <pxr/usd/usd/timeCode.h>
25#include <pxr/usd/usdGeom/primvar.h>
26#include <pxr/usd/usdUtils/sparseValueWriter.h>
27
28#include <cstdint>
29#include <optional>
30#include <type_traits>
31
32namespace usdtokens {
33inline const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal);
34}
35
36namespace blender::io::usd {
37
38namespace detail {
39
40/* Until we can use C++20, implement our own version of std::is_layout_compatible.
41 * Types with compatible layouts can be exchanged much more efficiently than otherwise.
42 */
43template<class T, class U> struct is_layout_compatible : std::false_type {};
44
45template<> struct is_layout_compatible<float2, pxr::GfVec2f> : std::true_type {};
46template<> struct is_layout_compatible<float3, pxr::GfVec3f> : std::true_type {};
47
48template<> struct is_layout_compatible<pxr::GfVec2f, float2> : std::true_type {};
49template<> struct is_layout_compatible<pxr::GfVec3f, float3> : std::true_type {};
50
51/* Conversion utilities to convert a Blender type to an USD type. */
52template<typename From, typename To> inline To convert_value(const From value)
53{
54 return value;
55}
56
57template<> inline pxr::GfVec2f convert_value(const float2 value)
58{
59 return pxr::GfVec2f(value[0], value[1]);
60}
61template<> inline pxr::GfVec3f convert_value(const float3 value)
62{
63 return pxr::GfVec3f(value[0], value[1], value[2]);
64}
65template<> inline pxr::GfVec3f convert_value(const ColorGeometry4f value)
66{
67 return pxr::GfVec3f(value.r, value.g, value.b);
68}
69template<> inline pxr::GfVec4f convert_value(const ColorGeometry4f value)
70{
71 return pxr::GfVec4f(value.r, value.g, value.b, value.a);
72}
73template<> inline pxr::GfVec3f convert_value(const ColorGeometry4b value)
74{
75 ColorGeometry4f color4f = value.decode();
76 return pxr::GfVec3f(color4f.r, color4f.g, color4f.b);
77}
78template<> inline pxr::GfVec4f convert_value(const ColorGeometry4b value)
79{
80 ColorGeometry4f color4f = value.decode();
81 return pxr::GfVec4f(color4f.r, color4f.g, color4f.b, color4f.a);
82}
83template<> inline pxr::GfQuatf convert_value(const math::Quaternion value)
84{
85 return pxr::GfQuatf(value.w, value.x, value.y, value.z);
86}
87
88template<> inline float2 convert_value(const pxr::GfVec2f value)
89{
90 return float2(value[0], value[1]);
91}
92template<> inline float3 convert_value(const pxr::GfVec3f value)
93{
94 return float3(value[0], value[1], value[2]);
95}
96template<> inline ColorGeometry4f convert_value(const pxr::GfVec3f value)
97{
98 return ColorGeometry4f(value[0], value[1], value[2], 1.0f);
99}
100template<> inline ColorGeometry4f convert_value(const pxr::GfVec4f value)
101{
102 return ColorGeometry4f(value[0], value[1], value[2], value[3]);
103}
104template<> inline math::Quaternion convert_value(const pxr::GfQuatf value)
105{
106 const pxr::GfVec3f &img = value.GetImaginary();
107 return math::Quaternion(value.GetReal(), img[0], img[1], img[2]);
108}
109
110} // namespace detail
111
112std::optional<pxr::SdfValueTypeName> convert_blender_type_to_usd(
113 const eCustomDataType blender_type, bool use_color3f_type = false);
114
115std::optional<eCustomDataType> convert_usd_type_to_blender(const pxr::SdfValueTypeName usd_type);
116
117/* Copy a typed Blender attribute array into a typed USD primvar attribute. */
118template<typename BlenderT, typename USDT>
120 const pxr::UsdTimeCode timecode,
121 const pxr::UsdGeomPrimvar &primvar,
122 pxr::UsdUtilsSparseValueWriter &value_writer)
123{
124 constexpr bool is_same = std::is_same_v<BlenderT, USDT>;
125 constexpr bool is_compatible = detail::is_layout_compatible<BlenderT, USDT>::value;
126
127 pxr::VtArray<USDT> usd_data;
128 if (const std::optional<BlenderT> value = buffer.get_if_single()) {
129 usd_data.assign(buffer.size(), detail::convert_value<BlenderT, USDT>(*value));
130 }
131 else {
132 const VArraySpan<BlenderT> data(buffer);
133 if constexpr (is_same || is_compatible) {
134 usd_data.assign(data.template cast<USDT>().begin(), data.template cast<USDT>().end());
135 }
136 else {
137 usd_data.resize(data.size());
138 for (const int i : data.index_range()) {
139 usd_data[i] = detail::convert_value<BlenderT, USDT>(data[i]);
140 }
141 }
142 }
143
144 if (!primvar.HasValue()) {
145 primvar.Set(usd_data, pxr::UsdTimeCode::Default());
146 }
147
148 value_writer.SetAttribute(primvar.GetAttr(), usd_data, timecode);
149}
150
151void copy_blender_attribute_to_primvar(const GVArray &attribute,
152 const eCustomDataType data_type,
153 const pxr::UsdTimeCode timecode,
154 const pxr::UsdGeomPrimvar &primvar,
155 pxr::UsdUtilsSparseValueWriter &value_writer);
156
157template<typename T>
158pxr::VtArray<T> get_primvar_array(const pxr::UsdGeomPrimvar &primvar,
159 const pxr::UsdTimeCode timecode)
160{
161 pxr::VtValue primvar_val;
162 if (!primvar.ComputeFlattened(&primvar_val, timecode)) {
163 return {};
164 }
165
166 if (!primvar_val.CanCast<pxr::VtArray<T>>()) {
167 return {};
168 }
169
170 return primvar_val.Cast<pxr::VtArray<T>>().template UncheckedGet<pxr::VtArray<T>>();
171}
172
173template<typename USDT, typename BlenderT>
174void copy_primvar_to_blender_buffer(const pxr::UsdGeomPrimvar &primvar,
175 const pxr::UsdTimeCode timecode,
176 const OffsetIndices<int> faces,
177 MutableSpan<BlenderT> attribute)
178{
179 pxr::VtArray<USDT> usd_data = get_primvar_array<USDT>(primvar, timecode);
180 if (usd_data.empty()) {
181 return;
182 }
183
184 constexpr bool is_same = std::is_same_v<USDT, BlenderT>;
185 constexpr bool is_compatible = detail::is_layout_compatible<USDT, BlenderT>::value;
186
187 const pxr::TfToken pv_interp = primvar.GetInterpolation();
188 if (pv_interp == pxr::UsdGeomTokens->constant) {
189 /* For situations where there's only a single item, flood fill the object. */
190 attribute.fill(detail::convert_value<USDT, BlenderT>(usd_data[0]));
191 }
192 else if (pv_interp == pxr::UsdGeomTokens->faceVarying) {
193 if (!faces.is_empty()) {
194 /* Reverse the index order. */
195 for (const int i : faces.index_range()) {
196 const IndexRange face = faces[i];
197 for (int j : face.index_range()) {
198 const int rev_index = face.last(j);
199 attribute[face.start() + j] = detail::convert_value<USDT, BlenderT>(usd_data[rev_index]);
200 }
201 }
202 }
203 else {
204 if constexpr (is_same || is_compatible) {
205 const Span<USDT> src(usd_data.data(), usd_data.size());
206 attribute.copy_from(src.template cast<BlenderT>());
207 }
208 else {
209 for (const int64_t i : attribute.index_range()) {
210 attribute[i] = detail::convert_value<USDT, BlenderT>(usd_data[i]);
211 }
212 }
213 }
214 }
215 else {
216 /* Assume direct one-to-one mapping. */
217 if (usd_data.size() == attribute.size()) {
218 if constexpr (is_same || is_compatible) {
219 const Span<USDT> src(usd_data.data(), usd_data.size());
220 attribute.copy_from(src.template cast<BlenderT>());
221 }
222 else {
223 for (const int64_t i : attribute.index_range()) {
224 attribute[i] = detail::convert_value<USDT, BlenderT>(usd_data[i]);
225 }
226 }
227 }
228 }
229}
230
231void copy_primvar_to_blender_attribute(const pxr::UsdGeomPrimvar &primvar,
232 const pxr::UsdTimeCode timecode,
233 const eCustomDataType data_type,
234 const bke::AttrDomain domain,
235 const OffsetIndices<int> face_indices,
237
238} // namespace blender::io::usd
ChannelStorageType r
Definition BLI_color.hh:88
ChannelStorageType g
Definition BLI_color.hh:88
ChannelStorageType b
Definition BLI_color.hh:88
ChannelStorageType a
Definition BLI_color.hh:88
constexpr int64_t last(const int64_t n=0) const
std::optional< T > get_if_single() const
ccl_device_inline int4 cast(const float4 a)
Definition math_float4.h:29
To convert_value(const From value)
pxr::VtArray< T > get_primvar_array(const pxr::UsdGeomPrimvar &primvar, const pxr::UsdTimeCode timecode)
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)
void copy_primvar_to_blender_buffer(const pxr::UsdGeomPrimvar &primvar, const pxr::UsdTimeCode timecode, const OffsetIndices< int > faces, MutableSpan< BlenderT > attribute)
std::optional< eCustomDataType > convert_usd_type_to_blender(const pxr::SdfValueTypeName usd_type)
std::optional< pxr::SdfValueTypeName > convert_blender_type_to_usd(const eCustomDataType blender_type, bool use_color3f_type)
void copy_blender_buffer_to_primvar(const VArray< BlenderT > &buffer, const pxr::UsdTimeCode timecode, const pxr::UsdGeomPrimvar &primvar, pxr::UsdUtilsSparseValueWriter &value_writer)
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)
QuaternionBase< float > Quaternion
VecBase< float, 2 > float2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
VecBase< float, 3 > float3
const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal)
__int64 int64_t
Definition stdint.h:89