Alembic: rotation mode issue in copy_m44_axis_swap, and added unit tests.
[blender-staging.git] / source / blender / alembic / intern / abc_util.cc
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 #include "abc_util.h"
24
25 #include "abc_camera.h"
26 #include "abc_curves.h"
27 #include "abc_mesh.h"
28 #include "abc_nurbs.h"
29 #include "abc_points.h"
30 #include "abc_transform.h"
31
32 #include <Alembic/AbcMaterial/IMaterial.h>
33
34 #include <algorithm>
35
36 extern "C" {
37 #include "DNA_object_types.h"
38
39 #include "BLI_math.h"
40
41 #include "PIL_time.h"
42 }
43
44 std::string get_id_name(const Object * const ob)
45 {
46         if (!ob) {
47                 return "";
48         }
49
50         return get_id_name(&ob->id);
51 }
52
53 std::string get_id_name(const ID * const id)
54 {
55         std::string name(id->name + 2);
56         std::replace(name.begin(), name.end(), ' ', '_');
57         std::replace(name.begin(), name.end(), '.', '_');
58         std::replace(name.begin(), name.end(), ':', '_');
59
60         return name;
61 }
62
63 std::string get_object_dag_path_name(const Object * const ob, Object *dupli_parent)
64 {
65         std::string name = get_id_name(ob);
66
67         Object *p = ob->parent;
68
69         while (p) {
70                 name = get_id_name(p) + "/" + name;
71                 p = p->parent;
72         }
73
74         if (dupli_parent && (ob != dupli_parent)) {
75                 name = get_id_name(dupli_parent) + "/" + name;
76         }
77
78         return name;
79 }
80
81 bool object_selected(Object *ob)
82 {
83         return ob->flag & SELECT;
84 }
85
86 bool parent_selected(Object *ob)
87 {
88         if (object_selected(ob)) {
89                 return true;
90         }
91
92         bool do_export = false;
93
94         Object *parent = ob->parent;
95
96         while (parent != NULL) {
97                 if (object_selected(parent)) {
98                         do_export = true;
99                         break;
100                 }
101
102                 parent = parent->parent;
103         }
104
105         return do_export;
106 }
107
108 Imath::M44d convert_matrix(float mat[4][4])
109 {
110         Imath::M44d m;
111
112         for (int i = 0; i < 4; ++i) {
113                 for (int j = 0; j < 4; ++j) {
114                         m[i][j] = mat[i][j];
115                 }
116         }
117
118         return m;
119 }
120
121 void split(const std::string &s, const char delim, std::vector<std::string> &tokens)
122 {
123         tokens.clear();
124
125         std::stringstream ss(s);
126         std::string item;
127
128         while (std::getline(ss, item, delim)) {
129                 if (!item.empty()) {
130                         tokens.push_back(item);
131                 }
132         }
133 }
134
135 void create_swapped_rotation_matrix(
136         float rot_x_mat[3][3], float rot_y_mat[3][3],
137         float rot_z_mat[3][3], const float euler[3],
138         AbcAxisSwapMode mode)
139 {
140         const float rx = euler[0];
141         float ry;
142         float rz;
143
144         /* Apply transformation */
145         switch(mode) {
146                 case ABC_ZUP_FROM_YUP:
147                         ry = -euler[2];
148                         rz = euler[1];
149                         break;
150                 case ABC_YUP_FROM_ZUP:
151                         ry = euler[2];
152                         rz = -euler[1];
153                         break;
154                 default:
155                         BLI_assert(false);
156         }
157
158         unit_m3(rot_x_mat);
159         unit_m3(rot_y_mat);
160         unit_m3(rot_z_mat);
161
162         rot_x_mat[1][1] = cos(rx);
163         rot_x_mat[2][1] = -sin(rx);
164         rot_x_mat[1][2] = sin(rx);
165         rot_x_mat[2][2] = cos(rx);
166
167         rot_y_mat[2][2] = cos(ry);
168         rot_y_mat[0][2] = -sin(ry);
169         rot_y_mat[2][0] = sin(ry);
170         rot_y_mat[0][0] = cos(ry);
171
172         rot_z_mat[0][0] = cos(rz);
173         rot_z_mat[1][0] = -sin(rz);
174         rot_z_mat[0][1] = sin(rz);
175         rot_z_mat[1][1] = cos(rz);
176 }
177
178 /* Convert matrix from Z=up to Y=up or vice versa. Use yup_mat = zup_mat for in-place conversion. */
179 void copy_m44_axis_swap(float dst_mat[4][4], float src_mat[4][4], AbcAxisSwapMode mode)
180 {
181         float dst_rot[3][3], src_rot[3][3], dst_scale_mat[4][4];
182         float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3];
183         float src_trans[3], dst_scale[3], src_scale[3], euler[3];
184
185         zero_v3(src_trans);
186         zero_v3(dst_scale);
187         zero_v3(src_scale);
188         zero_v3(euler);
189         unit_m3(src_rot);
190         unit_m3(dst_rot);
191         unit_m4(dst_scale_mat);
192
193         /* We assume there is no sheer component and no homogeneous scaling component. */
194         BLI_assert(fabs(src_mat[0][3]) < 2 * FLT_EPSILON);
195         BLI_assert(fabs(src_mat[1][3]) < 2 * FLT_EPSILON);
196         BLI_assert(fabs(src_mat[2][3]) < 2 * FLT_EPSILON);
197         BLI_assert(fabs(src_mat[3][3] - 1.0f) < 2 * FLT_EPSILON);
198
199         /* Extract translation, rotation, and scale form matrix. */
200         mat4_to_loc_rot_size(src_trans, src_rot, src_scale, src_mat);
201
202         /* Get euler angles from rotation matrix. */
203         mat3_to_eulO(euler, ROT_MODE_XZY, src_rot);
204
205         /* Create X, Y, Z rotation matrices from euler angles. */
206         create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, mode);
207
208         /* Concatenate rotation matrices. */
209         mul_m3_m3m3(dst_rot, dst_rot, rot_z_mat);
210         mul_m3_m3m3(dst_rot, dst_rot, rot_y_mat);
211         mul_m3_m3m3(dst_rot, dst_rot, rot_x_mat);
212
213         mat3_to_eulO(euler, ROT_MODE_XZY, dst_rot);
214
215         /* Start construction of dst_mat from rotation matrix */
216         unit_m4(dst_mat);
217         copy_m4_m3(dst_mat, dst_rot);
218
219         /* Apply translation */
220         switch(mode) {
221                 case ABC_ZUP_FROM_YUP:
222                         copy_zup_from_yup(dst_mat[3], src_trans);
223                         break;
224                 case ABC_YUP_FROM_ZUP:
225                         copy_yup_from_zup(dst_mat[3], src_trans);
226                         break;
227                 default:
228                         BLI_assert(false);
229         }
230
231         /* Apply scale matrix. Swaps y and z, but does not
232          * negate like translation does. */
233         dst_scale[0] = src_scale[0];
234         dst_scale[1] = src_scale[2];
235         dst_scale[2] = src_scale[1];
236
237         size_to_mat4(dst_scale_mat, dst_scale);
238         mul_m4_m4m4(dst_mat, dst_mat, dst_scale_mat);
239 }
240
241 void convert_matrix(const Imath::M44d &xform, Object *ob, float r_mat[4][4])
242 {
243         for (int i = 0; i < 4; ++i) {
244                 for (int j = 0; j < 4; ++j) {
245                         r_mat[i][j] = static_cast<float>(xform[i][j]);
246                 }
247         }
248
249         if (ob->type == OB_CAMERA) {
250                 float cam_to_yup[4][4];
251                 axis_angle_to_mat4_single(cam_to_yup, 'X', M_PI_2);
252                 mul_m4_m4m4(r_mat, r_mat, cam_to_yup);
253         }
254
255         copy_m44_axis_swap(r_mat, r_mat, ABC_ZUP_FROM_YUP);
256 }
257
258 /* Recompute transform matrix of object in new coordinate system
259  * (from Z-Up to Y-Up). */
260 void create_transform_matrix(Object *obj, float r_yup_mat[4][4])
261 {
262         float zup_mat[4][4];
263
264         /* get local matrix. */
265         /* TODO Sybren: when we're exporting as "flat", i.e. non-hierarchial,
266          * we should export the world matrix even when the object has a parent
267          * Blender Object. */
268         if (obj->parent) {
269                 /* Note that this produces another matrix than the local matrix, due to
270                  * constraints and modifiers as well as the obj->parentinv matrix. */
271                 invert_m4_m4(obj->parent->imat, obj->parent->obmat);
272                 mul_m4_m4m4(zup_mat, obj->parent->imat, obj->obmat);
273                 copy_m44_axis_swap(r_yup_mat, zup_mat, ABC_YUP_FROM_ZUP);
274         }
275         else {
276                 copy_m44_axis_swap(r_yup_mat, obj->obmat, ABC_YUP_FROM_ZUP);
277         }
278 }
279
280 bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name)
281 {
282         if (!prop.valid()) {
283                 return false;
284         }
285
286         return prop.getPropertyHeader(name) != NULL;
287 }
288
289 typedef std::pair<Alembic::AbcCoreAbstract::index_t, float> index_time_pair_t;
290
291 float get_weight_and_index(float time,
292                            const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling,
293                            int samples_number,
294                            Alembic::AbcGeom::index_t &i0,
295                            Alembic::AbcGeom::index_t &i1)
296 {
297         samples_number = std::max(samples_number, 1);
298
299         index_time_pair_t t0 = time_sampling->getFloorIndex(time, samples_number);
300         i0 = i1 = t0.first;
301
302         if (samples_number == 1 || (fabs(time - t0.second) < 0.0001f)) {
303                 return 0.0f;
304         }
305
306         index_time_pair_t t1 = time_sampling->getCeilIndex(time, samples_number);
307         i1 = t1.first;
308
309         if (i0 == i1) {
310                 return 0.0f;
311         }
312
313         const float bias = (time - t0.second) / (t1.second - t0.second);
314
315         if (fabs(1.0f - bias) < 0.0001f) {
316                 i0 = i1;
317                 return 0.0f;
318         }
319
320         return bias;
321 }
322
323 //#define USE_NURBS
324
325 AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSettings &settings)
326 {
327         AbcObjectReader *reader = NULL;
328
329         const Alembic::AbcGeom::MetaData &md = object.getMetaData();
330
331         if (Alembic::AbcGeom::IXform::matches(md)) {
332                 reader = new AbcEmptyReader(object, settings);
333         }
334         else if (Alembic::AbcGeom::IPolyMesh::matches(md)) {
335                 reader = new AbcMeshReader(object, settings);
336         }
337         else if (Alembic::AbcGeom::ISubD::matches(md)) {
338                 reader = new AbcSubDReader(object, settings);
339         }
340         else if (Alembic::AbcGeom::INuPatch::matches(md)) {
341 #ifdef USE_NURBS
342                 /* TODO(kevin): importing cyclic NURBS from other software crashes
343                  * at the moment. This is due to the fact that NURBS in other
344                  * software have duplicated points which causes buffer overflows in
345                  * Blender. Need to figure out exactly how these points are
346                  * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV).
347                  * Until this is fixed, disabling NURBS reading. */
348                 reader = new AbcNurbsReader(child, settings);
349 #endif
350         }
351         else if (Alembic::AbcGeom::ICamera::matches(md)) {
352                 reader = new AbcCameraReader(object, settings);
353         }
354         else if (Alembic::AbcGeom::IPoints::matches(md)) {
355                 reader = new AbcPointsReader(object, settings);
356         }
357         else if (Alembic::AbcMaterial::IMaterial::matches(md)) {
358                 /* Pass for now. */
359         }
360         else if (Alembic::AbcGeom::ILight::matches(md)) {
361                 /* Pass for now. */
362         }
363         else if (Alembic::AbcGeom::IFaceSet::matches(md)) {
364                 /* Pass, those are handled in the mesh reader. */
365         }
366         else if (Alembic::AbcGeom::ICurves::matches(md)) {
367                 reader = new AbcCurveReader(object, settings);
368         }
369         else {
370                 std::cerr << "Alembic: unknown how to handle objects of schema "
371                           << md.get("schemaObjTitle")
372                           << ", skipping object "
373                           << object.getFullName() << std::endl;
374         }
375
376         return reader;
377 }
378
379 /* ********************** */
380
381 ScopeTimer::ScopeTimer(const char *message)
382         : m_message(message)
383         , m_start(PIL_check_seconds_timer())
384 {}
385
386 ScopeTimer::~ScopeTimer()
387 {
388         fprintf(stderr, "%s: %fs\n", m_message, PIL_check_seconds_timer() - m_start);
389 }
390
391 /* ********************** */
392
393 bool SimpleLogger::empty()
394 {
395         return ((size_t)m_stream.tellp()) == 0ul;
396 }
397
398 std::string SimpleLogger::str() const
399 {
400         return m_stream.str();
401 }
402
403 void SimpleLogger::clear()
404 {
405         m_stream.clear();
406         m_stream.str("");
407 }
408
409 std::ostringstream &SimpleLogger::stream()
410 {
411         return m_stream;
412 }
413
414 std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger)
415 {
416         os << logger.str();
417         return os;
418 }