Blender V5.0
usd_reader_xform.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Tangent Animation. All rights reserved.
2 * SPDX-FileCopyrightText: 2023 Blender Authors
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 *
6 * Adapted from the Blender Alembic importer implementation. */
7
8#include "usd_reader_xform.hh"
9
10#include "BKE_constraint.h"
11#include "BKE_lib_id.hh"
12#include "BKE_object.hh"
13
14#include "BLI_math_matrix.h"
15#include "BLI_string.h"
16
17#include "DNA_cachefile_types.h"
19#include "DNA_object_types.h"
20
21#include <pxr/base/gf/matrix4f.h>
22#include <pxr/usd/usdGeom/xformable.h>
23
24#include <string>
25
26namespace blender::io::usd {
27
29{
31 object_->empty_drawsize = 0.1f;
32 object_->data = nullptr;
33}
34
35void USDXformReader::read_object_data(Main * /*bmain*/, const pxr::UsdTimeCode time)
36{
37 bool is_constant;
38 float transform_from_usd[4][4];
39
40 read_matrix(transform_from_usd, time, settings_->scene_scale, &is_constant);
41
42 if (!is_constant && settings_->get_cache_file) {
46
47 pxr::SdfPath object_path = use_parent_xform_ ? prim_.GetParent().GetPath() : this->prim_path();
48
49 STRNCPY(data->object_path, object_path.GetAsString().c_str());
50
51 data->cache_file = settings_->get_cache_file();
52 id_us_plus(&data->cache_file->id);
53 }
54
55 BKE_object_apply_mat4(object_, transform_from_usd, true, false);
56
57 /* Make sure to collect custom attributes */
59}
60
62{
63 return get_xformable().GetPrim().GetPath();
64}
65
66void USDXformReader::read_matrix(float r_mat[4][4] /* local matrix */,
67 const pxr::UsdTimeCode time,
68 const float scale,
69 bool *r_is_constant) const
70{
71 BLI_assert(r_mat);
72 BLI_assert(r_is_constant);
73
74 *r_is_constant = true;
75 unit_m4(r_mat);
76
77 std::optional<XformResult> xf_result = get_local_usd_xform(time);
78
79 if (!xf_result) {
80 return;
81 }
82
83 std::get<0>(*xf_result).Get(r_mat);
84 *r_is_constant = std::get<1>(*xf_result);
85
86 /* Apply global scaling and rotation only to root objects, parenting
87 * will propagate it. */
88 if ((scale != 1.0 || settings_->do_convert_mat) && is_root_xform_) {
89
90 if (scale != 1.0f) {
91 float scale_mat[4][4];
92 scale_m4_fl(scale_mat, scale);
93 mul_m4_m4m4(r_mat, scale_mat, r_mat);
94 }
95
96 if (settings_->do_convert_mat) {
97 mul_m4_m4m4(r_mat, settings_->conversion_mat, r_mat);
98 }
99 }
100}
101
103{
104 const pxr::UsdGeomXformable xformable(prim_);
105
106 if (!xformable) {
107 /* This might happen if the prim is a Scope. */
108 return false;
109 }
110
111 bool reset_xform_stack = false;
112
113 return !xformable.GetOrderedXformOps(&reset_xform_stack).empty();
114}
115
117{
118 if (!prim_.IsValid()) {
119 return false;
120 }
121
122 if (is_in_proto()) {
123 /* We don't consider prototypes to be root prims,
124 * because we never want to apply global scaling
125 * or rotations to the prototypes themselves. */
126 return false;
127 }
128
129 if (prim_.IsA<pxr::UsdGeomXformable>()) {
130 /* If this prim doesn't have an ancestor that's a
131 * UsdGeomXformable, then it's a root prim. Note
132 * that it's not sufficient to only check the immediate
133 * parent prim, since the immediate parent could be a
134 * UsdGeomScope that has an xformable ancestor. */
135 pxr::UsdPrim cur_parent = prim_.GetParent();
136
137 if (use_parent_xform_) {
138 cur_parent = cur_parent.GetParent();
139 }
140
141 while (cur_parent && !cur_parent.IsPseudoRoot()) {
142 if (cur_parent.IsA<pxr::UsdGeomXformable>()) {
143 return false;
144 }
145 cur_parent = cur_parent.GetParent();
146 }
147
148 /* We didn't find an xformable ancestor. */
149 return true;
150 }
151
152 return false;
153}
154
155std::optional<XformResult> USDXformReader::get_local_usd_xform(const pxr::UsdTimeCode time) const
156{
157 const pxr::UsdGeomXformable xformable = get_xformable();
158
159 if (!xformable) {
160 /* This might happen if the prim is a Scope. */
161 return std::nullopt;
162 }
163
164 bool is_constant = !xformable.TransformMightBeTimeVarying();
165
166 bool reset_xform_stack;
167 pxr::GfMatrix4d xform;
168 if (!xformable.GetLocalTransformation(&xform, &reset_xform_stack, time)) {
169 return std::nullopt;
170 }
171
172 /* The USD bind transform is a matrix of doubles,
173 * but we cast it to GfMatrix4f because Blender expects
174 * a matrix of floats. */
175 return XformResult(pxr::GfMatrix4f(xform), is_constant);
176}
177
178pxr::UsdGeomXformable USDXformReader::get_xformable() const
179{
180 pxr::UsdPrim prim = use_parent_xform_ ? prim_.GetParent() : prim_;
181 return pxr::UsdGeomXformable(prim);
182}
183} // namespace blender::io::usd
struct bConstraint * BKE_constraint_add_for_object(struct Object *ob, const char *name, short type)
void id_us_plus(ID *id)
Definition lib_id.cc:358
General operations, lookup, etc. for blender objects.
void BKE_object_apply_mat4(Object *ob, const float mat[4][4], bool use_compat, bool use_parent)
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
#define BLI_assert(a)
Definition BLI_assert.h:46
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void scale_m4_fl(float R[4][4], float scale)
void unit_m4(float m[4][4])
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
@ CONSTRAINT_TYPE_TRANSFORM_CACHE
Object is a sort of wrapper for general info.
@ OB_EMPTY
BMesh const char void * data
const pxr::UsdPrim & prim() const
const ImportSettings * settings_
void set_props(bool merge_with_parent=false, pxr::UsdTimeCode time=pxr::UsdTimeCode::Default())
void create_object(Main *bmain) override
void read_object_data(Main *bmain, pxr::UsdTimeCode time) override
virtual std::optional< XformResult > get_local_usd_xform(pxr::UsdTimeCode time) const
void read_matrix(float r_mat[4][4], pxr::UsdTimeCode time, float scale, bool *r_is_constant) const
pxr::SdfPath object_prim_path() const override
std::tuple< pxr::GfMatrix4f, bool > XformResult