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