Blender V5.0
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
8
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_string.h"
28
29using Alembic::AbcGeom::IObject;
30using Alembic::AbcGeom::IXform;
31using Alembic::AbcGeom::IXformSchema;
32
33namespace blender::io::alembic {
34
35AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings)
38 m_settings(&settings),
39 m_is_reading_a_file_sequence(settings.is_sequence),
40 m_min_time(std::numeric_limits<chrono_t>::max()),
41 m_max_time(std::numeric_limits<chrono_t>::min()),
42 m_refcount(0),
44{
45 m_name = object.getFullName();
46 std::vector<std::string> parts;
47 split(m_name, '/', parts);
48
49 if (parts.size() >= 2) {
50 m_object_name = parts[parts.size() - 2];
51 m_data_name = parts[parts.size() - 1];
52 }
53 else {
54 m_object_name = m_data_name = parts[parts.size() - 1];
55 }
56
58}
59
61{
62 m_inherits_xform = false;
63
64 IXform ixform = xform();
65 if (!ixform) {
66 return;
67 }
68
69 const IXformSchema &schema(ixform.getSchema());
70 if (!schema.valid()) {
71 std::cerr << "Alembic object " << ixform.getFullName() << " has an invalid schema."
72 << std::endl;
73 return;
74 }
75
76 m_inherits_xform = schema.getInheritsXforms();
77
78 IObject ixform_parent = ixform.getParent();
79 if (!ixform_parent.getParent()) {
80 /* The archive top object certainly is not a transform itself, so handle
81 * it as "no parent". */
82 m_inherits_xform = false;
83 }
84 else {
85 m_inherits_xform = ixform_parent && m_inherits_xform;
86 }
87}
88
89const IObject &AbcObjectReader::iobject() const
90{
91 return m_iobject;
92}
93
95{
96 return m_object;
97}
98
100{
101 m_object = ob;
102}
103
104static Imath::M44d blend_matrices(const Imath::M44d &m0,
105 const Imath::M44d &m1,
106 const double weight)
107{
108 float mat0[4][4], mat1[4][4], ret[4][4];
109
110 /* Cannot use Imath::M44d::getValue() since this returns a pointer to
111 * doubles and interp_m4_m4m4 expects pointers to floats. So need to convert
112 * the matrices manually.
113 */
114
115 convert_matrix_datatype(m0, mat0);
116 convert_matrix_datatype(m1, mat1);
117 interp_m4_m4m4(ret, mat0, mat1, float(weight));
119}
120
121Imath::M44d get_matrix(const IXformSchema &schema, const chrono_t time)
122{
123 Alembic::AbcGeom::ISampleSelector selector(time);
124
125 const std::optional<SampleInterpolationSettings> interpolation_settings =
127 selector, schema.getTimeSampling(), schema.getNumSamples());
128
129 if (!interpolation_settings.has_value()) {
130 /* No interpolation, just read the current time. */
131 Alembic::AbcGeom::XformSample s0;
132 schema.get(s0, selector);
133 return s0.getMatrix();
134 }
135
136 Alembic::AbcGeom::XformSample s0, s1;
137 schema.get(s0, Alembic::AbcGeom::ISampleSelector(interpolation_settings->index));
138 schema.get(s1, Alembic::AbcGeom::ISampleSelector(interpolation_settings->ceil_index));
139 return blend_matrices(s0.getMatrix(), s1.getMatrix(), interpolation_settings->weight);
140}
141
143 const Alembic::Abc::ISampleSelector & /*sample_sel*/,
144 int /*read_flag*/,
145 const char * /*velocity_name*/,
146 const float /*velocity_scale*/,
147 const char ** /*r_err_str*/)
148{
149}
150
151bool AbcObjectReader::topology_changed(const Mesh * /*existing_mesh*/,
152 const Alembic::Abc::ISampleSelector & /*sample_sel*/)
153{
154 /* The default implementation of read_mesh() just returns the original mesh, so never changes the
155 * topology. */
156 return false;
157}
158
160{
161 bool is_constant = false;
162 float transform_from_alembic[4][4];
163
164 /* If the parent is a camera, apply the inverse rotation to make up for the from-Maya rotation.
165 * This assumes that the parent object also was imported from Alembic. */
166 if (m_object->parent != nullptr && m_object->parent->type == OB_CAMERA) {
167 axis_angle_to_mat4_single(m_object->parentinv, 'X', -M_PI_2);
168 }
169
170 this->read_matrix(transform_from_alembic, time, m_settings->scale, is_constant);
171
172 /* Apply the matrix to the object. */
173 BKE_object_apply_mat4(m_object, transform_from_alembic, true, false);
174 BKE_object_to_mat4(m_object, m_object->runtime->object_to_world.ptr());
175
176 if (!is_constant || m_settings->always_add_cache_reader) {
180 STRNCPY(data->object_path, m_iobject.getFullName().c_str());
181
182 data->cache_file = m_settings->cache_file;
183 id_us_plus(&data->cache_file->id);
184 }
185}
186
187Alembic::AbcGeom::IXform AbcObjectReader::xform()
188{
189 /* Check that we have an empty object (locator, bone head/tail...). */
190 if (IXform::matches(m_iobject.getMetaData())) {
191 try {
192 return IXform(m_iobject, Alembic::AbcGeom::kWrapExisting);
193 }
194 catch (Alembic::Util::Exception &ex) {
195 printf("Alembic: error reading object transform for '%s': %s\n",
196 m_iobject.getFullName().c_str(),
197 ex.what());
198 return IXform();
199 }
200 }
201
202 /* Check that we have an object with actual data, in which case the
203 * parent Alembic object should contain the transform. */
204 IObject abc_parent = m_iobject.getParent();
205
206 /* The archive's top object can be recognized by not having a parent. */
207 if (abc_parent.getParent() && IXform::matches(abc_parent.getMetaData())) {
208 try {
209 return IXform(abc_parent, Alembic::AbcGeom::kWrapExisting);
210 }
211 catch (Alembic::Util::Exception &ex) {
212 printf("Alembic: error reading object transform for '%s': %s\n",
213 abc_parent.getFullName().c_str(),
214 ex.what());
215 return IXform();
216 }
217 }
218
219 /* This can happen in certain cases. For example, MeshLab exports
220 * point clouds without parent XForm. */
221 return IXform();
222}
223
224void AbcObjectReader::read_matrix(float r_mat[4][4] /* local matrix */,
225 const chrono_t time,
226 const float scale,
227 bool &r_is_constant)
228{
229 IXform ixform = xform();
230 if (!ixform) {
231 unit_m4(r_mat);
232 r_is_constant = true;
233 return;
234 }
235
236 const IXformSchema &schema(ixform.getSchema());
237 if (!schema.valid()) {
238 std::cerr << "Alembic object " << ixform.getFullName() << " has an invalid schema."
239 << std::endl;
240 return;
241 }
242
243 const Imath::M44d matrix = get_matrix(schema, time);
244 convert_matrix_datatype(matrix, r_mat);
246
247 /* Convert from Maya to Blender camera orientation. Children of this camera
248 * will have the opposite transform as their Parent Inverse matrix.
249 * See AbcObjectReader::setupObjectTransform(). */
250 if (m_object->type == OB_CAMERA) {
251 float camera_rotation[4][4];
252 axis_angle_to_mat4_single(camera_rotation, 'X', M_PI_2);
253 mul_m4_m4m4(r_mat, r_mat, camera_rotation);
254 }
255
256 if (!m_inherits_xform) {
257 /* Only apply scaling to root objects, parenting will propagate it. */
258 float scale_mat[4][4];
259 scale_m4_fl(scale_mat, scale);
260 mul_m4_m4m4(r_mat, scale_mat, r_mat);
261 }
262
263 r_is_constant = schema.isConstant();
264}
265
267{
269 BLI_addtail(&m_object->modifiers, md);
271
272 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
273
274 mcmd->cache_file = m_settings->cache_file;
275 id_us_plus(&mcmd->cache_file->id);
276
277 STRNCPY(mcmd->object_path, m_iobject.getFullName().c_str());
278}
279
281{
282 return m_min_time;
283}
284
286{
287 return m_max_time;
288}
289
291{
292 return m_refcount;
293}
294
296{
297 m_refcount++;
298}
299
301{
302 m_refcount--;
304}
305
306} // 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:358
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:46
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
#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 scale_m4_fl(float R[4][4], float scale)
void unit_m4(float m[4][4])
void axis_angle_to_mat4_single(float R[4][4], char axis, float angle)
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
@ CONSTRAINT_TYPE_TRANSFORM_CACHE
@ eModifierType_MeshSequenceCache
Object is a sort of wrapper for general info.
@ OB_CAMERA
BMesh const char void * data
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:39
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:122
void split(const std::string &s, const char delim, std::vector< std::string > &tokens)
Definition abc_util.cc:61
return ret
#define min(a, b)
Definition sort.cc:36
max
Definition text_draw.cc:251