Blender V4.3
abc_reader_object.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "abc_reader_object.h"
10#include "abc_axis_conversion.h"
11#include "abc_util.h"
12
13#include "DNA_cachefile_types.h"
15#include "DNA_modifier_types.h"
16#include "DNA_object_types.h"
17
18#include "BKE_constraint.h"
19#include "BKE_lib_id.hh"
20#include "BKE_modifier.hh"
21#include "BKE_object.hh"
22#include "BKE_object_types.hh"
23
24#include "BLI_listbase.h"
25#include "BLI_math_matrix.h"
26#include "BLI_math_rotation.h"
27#include "BLI_math_vector.h"
28#include "BLI_string.h"
29
30using Alembic::AbcGeom::IObject;
31using Alembic::AbcGeom::IXform;
32using Alembic::AbcGeom::IXformSchema;
33
34namespace blender::io::alembic {
35
36AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings)
37 : m_object(nullptr),
38 m_iobject(object),
39 m_settings(&settings),
40 m_is_reading_a_file_sequence(settings.is_sequence),
41 m_min_time(std::numeric_limits<chrono_t>::max()),
42 m_max_time(std::numeric_limits<chrono_t>::min()),
43 m_refcount(0),
44 parent_reader(nullptr)
45{
46 m_name = object.getFullName();
47 std::vector<std::string> parts;
48 split(m_name, '/', parts);
49
50 if (parts.size() >= 2) {
51 m_object_name = parts[parts.size() - 2];
52 m_data_name = parts[parts.size() - 1];
53 }
54 else {
55 m_object_name = m_data_name = parts[parts.size() - 1];
56 }
57
59}
60
62{
63 m_inherits_xform = false;
64
65 IXform ixform = xform();
66 if (!ixform) {
67 return;
68 }
69
70 const IXformSchema &schema(ixform.getSchema());
71 if (!schema.valid()) {
72 std::cerr << "Alembic object " << ixform.getFullName() << " has an invalid schema."
73 << std::endl;
74 return;
75 }
76
77 m_inherits_xform = schema.getInheritsXforms();
78
79 IObject ixform_parent = ixform.getParent();
80 if (!ixform_parent.getParent()) {
81 /* The archive top object certainly is not a transform itself, so handle
82 * it as "no parent". */
83 m_inherits_xform = false;
84 }
85 else {
86 m_inherits_xform = ixform_parent && m_inherits_xform;
87 }
88}
89
90const IObject &AbcObjectReader::iobject() const
91{
92 return m_iobject;
93}
94
96{
97 return m_object;
98}
99
101{
102 m_object = ob;
103}
104
105static Imath::M44d blend_matrices(const Imath::M44d &m0,
106 const Imath::M44d &m1,
107 const double weight)
108{
109 float mat0[4][4], mat1[4][4], ret[4][4];
110
111 /* Cannot use Imath::M44d::getValue() since this returns a pointer to
112 * doubles and interp_m4_m4m4 expects pointers to floats. So need to convert
113 * the matrices manually.
114 */
115
116 convert_matrix_datatype(m0, mat0);
117 convert_matrix_datatype(m1, mat1);
118 interp_m4_m4m4(ret, mat0, mat1, float(weight));
120}
121
122Imath::M44d get_matrix(const IXformSchema &schema, const chrono_t time)
123{
124 Alembic::AbcGeom::ISampleSelector selector(time);
125
126 const std::optional<SampleInterpolationSettings> interpolation_settings =
128 selector, schema.getTimeSampling(), schema.getNumSamples());
129
130 if (!interpolation_settings.has_value()) {
131 /* No interpolation, just read the current time. */
132 Alembic::AbcGeom::XformSample s0;
133 schema.get(s0, selector);
134 return s0.getMatrix();
135 }
136
137 Alembic::AbcGeom::XformSample s0, s1;
138 schema.get(s0, Alembic::AbcGeom::ISampleSelector(interpolation_settings->index));
139 schema.get(s1, Alembic::AbcGeom::ISampleSelector(interpolation_settings->ceil_index));
140 return blend_matrices(s0.getMatrix(), s1.getMatrix(), interpolation_settings->weight);
141}
142
144 const Alembic::Abc::ISampleSelector & /*sample_sel*/,
145 int /*read_flag*/,
146 const char * /*velocity_name*/,
147 const float /*velocity_scale*/,
148 const char ** /*r_err_str*/)
149{
150}
151
152bool AbcObjectReader::topology_changed(const Mesh * /*existing_mesh*/,
153 const Alembic::Abc::ISampleSelector & /*sample_sel*/)
154{
155 /* The default implementation of read_mesh() just returns the original mesh, so never changes the
156 * topology. */
157 return false;
158}
159
161{
162 bool is_constant = false;
163 float transform_from_alembic[4][4];
164
165 /* If the parent is a camera, apply the inverse rotation to make up for the from-Maya rotation.
166 * This assumes that the parent object also was imported from Alembic. */
167 if (m_object->parent != nullptr && m_object->parent->type == OB_CAMERA) {
169 }
170
171 this->read_matrix(transform_from_alembic, time, m_settings->scale, is_constant);
172
173 /* Apply the matrix to the object. */
174 BKE_object_apply_mat4(m_object, transform_from_alembic, true, false);
175 BKE_object_to_mat4(m_object, m_object->runtime->object_to_world.ptr());
176
177 if (!is_constant || m_settings->always_add_cache_reader) {
180 bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data);
181 STRNCPY(data->object_path, m_iobject.getFullName().c_str());
182
183 data->cache_file = m_settings->cache_file;
184 id_us_plus(&data->cache_file->id);
185 }
186}
187
188Alembic::AbcGeom::IXform AbcObjectReader::xform()
189{
190 /* Check that we have an empty object (locator, bone head/tail...). */
191 if (IXform::matches(m_iobject.getMetaData())) {
192 try {
193 return IXform(m_iobject, Alembic::AbcGeom::kWrapExisting);
194 }
195 catch (Alembic::Util::Exception &ex) {
196 printf("Alembic: error reading object transform for '%s': %s\n",
197 m_iobject.getFullName().c_str(),
198 ex.what());
199 return IXform();
200 }
201 }
202
203 /* Check that we have an object with actual data, in which case the
204 * parent Alembic object should contain the transform. */
205 IObject abc_parent = m_iobject.getParent();
206
207 /* The archive's top object can be recognized by not having a parent. */
208 if (abc_parent.getParent() && IXform::matches(abc_parent.getMetaData())) {
209 try {
210 return IXform(abc_parent, Alembic::AbcGeom::kWrapExisting);
211 }
212 catch (Alembic::Util::Exception &ex) {
213 printf("Alembic: error reading object transform for '%s': %s\n",
214 abc_parent.getFullName().c_str(),
215 ex.what());
216 return IXform();
217 }
218 }
219
220 /* This can happen in certain cases. For example, MeshLab exports
221 * point clouds without parent XForm. */
222 return IXform();
223}
224
225void AbcObjectReader::read_matrix(float r_mat[4][4] /* local matrix */,
226 const chrono_t time,
227 const float scale,
228 bool &r_is_constant)
229{
230 IXform ixform = xform();
231 if (!ixform) {
232 unit_m4(r_mat);
233 r_is_constant = true;
234 return;
235 }
236
237 const IXformSchema &schema(ixform.getSchema());
238 if (!schema.valid()) {
239 std::cerr << "Alembic object " << ixform.getFullName() << " has an invalid schema."
240 << std::endl;
241 return;
242 }
243
244 const Imath::M44d matrix = get_matrix(schema, time);
245 convert_matrix_datatype(matrix, r_mat);
247
248 /* Convert from Maya to Blender camera orientation. Children of this camera
249 * will have the opposite transform as their Parent Inverse matrix.
250 * See AbcObjectReader::setupObjectTransform(). */
251 if (m_object->type == OB_CAMERA) {
252 float camera_rotation[4][4];
253 axis_angle_to_mat4_single(camera_rotation, 'X', M_PI_2);
254 mul_m4_m4m4(r_mat, r_mat, camera_rotation);
255 }
256
257 if (!m_inherits_xform) {
258 /* Only apply scaling to root objects, parenting will propagate it. */
259 float scale_mat[4][4];
260 scale_m4_fl(scale_mat, scale);
261 mul_m4_m4m4(r_mat, scale_mat, r_mat);
262 }
263
264 r_is_constant = schema.isConstant();
265}
266
280
282{
283 return m_min_time;
284}
285
287{
288 return m_max_time;
289}
290
292{
293 return m_refcount;
294}
295
297{
298 m_refcount++;
299}
300
302{
303 m_refcount--;
305}
306
307} // namespace blender::io::alembic
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:351
void BKE_modifiers_persistent_uid_init(const Object &object, ModifierData &md)
ModifierData * BKE_modifier_new(int type)
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)
void BKE_object_to_mat4(const Object *ob, float r_mat[4][4])
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
#define M_PI_2
void interp_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4], float t)
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void unit_m4(float m[4][4])
Definition rct.c:1127
void scale_m4_fl(float R[4][4], float scale)
void axis_angle_to_mat4_single(float R[4][4], char axis, float angle)
#define STRNCPY(dst, src)
Definition BLI_string.h:593
@ CONSTRAINT_TYPE_TRANSFORM_CACHE
@ eModifierType_MeshSequenceCache
Object is a sort of wrapper for general info.
@ OB_CAMERA
AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
void read_matrix(float r_mat[4][4], chrono_t time, float scale, bool &is_constant)
const Alembic::Abc::IObject & iobject() const
virtual bool topology_changed(const Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel)
virtual Alembic::AbcGeom::IXform xform()
virtual void read_geometry(bke::GeometrySet &geometry_set, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char *velocity_name, float velocity_scale, const char **r_err_str)
#define printf
Imath::M44d get_matrix(const IXformSchema &schema, const chrono_t time)
void copy_m44_axis_swap(float dst_mat[4][4], float src_mat[4][4], AbcAxisSwapMode mode)
static Imath::M44d blend_matrices(const Imath::M44d &m0, const Imath::M44d &m1, const double weight)
Imath::M44d convert_matrix_datatype(const float mat[4][4])
Definition abc_util.cc:74
std::optional< SampleInterpolationSettings > get_sample_interpolation_settings(const Alembic::AbcGeom::ISampleSelector &selector, const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling, size_t samples_number)
Definition abc_util.cc:157
void split(const std::string &s, const char delim, std::vector< std::string > &tokens)
Definition abc_util.cc:96
return ret
#define min(a, b)
Definition sort.c:32
ObjectRuntimeHandle * runtime
ListBase modifiers
float parentinv[4][4]
struct Object * parent