Camera tracking: libmv distortion API now also uses camera intrinsics
[blender.git] / source / blender / modifiers / intern / MOD_meshcache_mdd.c
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): Campbell Barton, pkowal
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/modifiers/intern/MOD_meshcache_mdd.c
24  *  \ingroup modifiers
25  */
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30
31 #include "BLO_sys_types.h"
32 #include "BLI_utildefines.h"
33 #include "BLI_endian_switch.h"
34 #include "BLI_fileops.h"
35 #include "BLI_math.h"
36
37 #include "MOD_meshcache_util.h"  /* own include */
38
39 #include "DNA_modifier_types.h"
40
41 typedef struct MDDHead {
42         int frame_tot;
43         int verts_tot;
44 } MDDHead;  /* frames, verts */
45
46 static bool meshcache_read_mdd_head(FILE *fp, const int verts_tot,
47                                     MDDHead *mdd_head,
48                                     const char **err_str)
49 {
50         if (!fread(mdd_head, sizeof(*mdd_head), 1, fp)) {
51                 *err_str = "Missing header";
52                 return false;
53         }
54
55 #ifdef __LITTLE_ENDIAN__
56         BLI_endian_switch_int32_array((int *)mdd_head, 2);
57 #endif
58
59         if (mdd_head->verts_tot != verts_tot) {
60                 *err_str = "Vertex count mismatch";
61                 return false;
62         }
63
64         if (mdd_head->frame_tot <= 0) {
65                 *err_str = "Invalid frame total";
66                 return false;
67         }
68         /* intentionally dont seek back */
69
70         return true;
71 }
72
73 /**
74  * Gets the index frange and factor
75  */
76 static bool meshcache_read_mdd_range(FILE *fp,
77                                      const int verts_tot,
78                                      const float frame, const char interp,
79                                      int r_index_range[2], float *r_factor,
80                                      const char **err_str)
81 {
82         MDDHead mdd_head;
83
84         /* first check interpolation and get the vert locations */
85
86         if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
87                 return false;
88         }
89
90         MOD_meshcache_calc_range(frame, interp, mdd_head.frame_tot, r_index_range, r_factor);
91
92         return true;
93 }
94
95 static bool meshcache_read_mdd_range_from_time(FILE *fp,
96                                                const int verts_tot,
97                                                const float time, const float UNUSED(fps),
98                                                float *r_frame,
99                                                const char **err_str)
100 {
101         MDDHead mdd_head;
102         int i;
103         float f_time, f_time_prev = FLT_MAX;
104         float frame;
105
106         if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
107                 return false;
108         }
109
110         for (i = 0; i < mdd_head.frame_tot; i++) {
111                 fread(&f_time, sizeof(float), 1, fp);
112 #ifdef __LITTLE_ENDIAN__
113                 BLI_endian_switch_float(&f_time);
114 #endif
115                 if (f_time >= time) {
116                         break;
117                 }
118                 f_time_prev = f_time;
119         }
120
121         if (i == mdd_head.frame_tot) {
122                 frame = (float)(mdd_head.frame_tot - 1);
123         }
124         if (UNLIKELY(f_time_prev == FLT_MAX)) {
125                 frame = 0.0f;
126         }
127         else {
128                 const float range  = f_time - f_time_prev;
129
130                 if (range <= FRAME_SNAP_EPS) {
131                         frame = (float)i;
132                 }
133                 else {
134                         frame = (float)(i - 1) + ((time - f_time_prev) / range);
135                 }
136         }
137
138         *r_frame = frame;
139         return true;
140 }
141
142 bool MOD_meshcache_read_mdd_index(FILE *fp,
143                                   float (*vertexCos)[3], const int verts_tot,
144                                   const int index, const float factor,
145                                   const char **err_str)
146 {
147         MDDHead mdd_head;
148
149         if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
150                 return false;
151         }
152
153         if (fseek(fp, mdd_head.frame_tot * sizeof(int), SEEK_CUR) != 0) {
154                 *err_str = "Header seek failed";
155                 return false;
156         }
157
158         if (fseek(fp, index * mdd_head.verts_tot * sizeof(float) * 3, SEEK_CUR) != 0) {
159                 *err_str = "Failed to seek frame";
160                 return false;
161         }
162
163         if (factor >= 1.0f) {
164 #if 1
165                 float *vco = *vertexCos;
166                 unsigned int i;
167                 for (i = mdd_head.verts_tot; i != 0 ; i--, vco += 3) {
168                         fread(vco, sizeof(float) * 3, 1, fp);
169
170 #  ifdef __LITTLE_ENDIAN__
171                         BLI_endian_switch_float(vco + 0);
172                         BLI_endian_switch_float(vco + 1);
173                         BLI_endian_switch_float(vco + 2);
174 #  endif  /* __LITTLE_ENDIAN__ */
175                 }
176 #else
177                 /* no blending */
178                 if (!fread(vertexCos, sizeof(float) * 3, mdd_head.verts_tot, f)) {
179                         *err_str = errno ? strerror(errno) : "Failed to read frame";
180                         return false;
181                 }
182 #  ifdef __LITTLE_ENDIAN__
183                 BLI_endian_switch_float_array(vertexCos[0], mdd_head.verts_tot * 3);
184 #  endif
185 #endif
186         }
187         else {
188                 const float ifactor = 1.0f - factor;
189                 float *vco = *vertexCos;
190                 unsigned int i;
191                 for (i = mdd_head.verts_tot; i != 0 ; i--, vco += 3) {
192                         float tvec[3];
193                         fread(tvec, sizeof(float) * 3, 1, fp);
194
195 #ifdef __LITTLE_ENDIAN__
196                         BLI_endian_switch_float(tvec + 0);
197                         BLI_endian_switch_float(tvec + 1);
198                         BLI_endian_switch_float(tvec + 2);
199 #endif
200
201                         vco[0] = (vco[0] * ifactor) + (tvec[0] * factor);
202                         vco[1] = (vco[1] * ifactor) + (tvec[1] * factor);
203                         vco[2] = (vco[2] * ifactor) + (tvec[2] * factor);
204                 }
205         }
206
207         return true;
208 }
209
210 bool MOD_meshcache_read_mdd_frame(FILE *fp,
211                                   float (*vertexCos)[3], const int verts_tot, const char interp,
212                                   const float frame,
213                                   const char **err_str)
214 {
215         int index_range[2];
216         float factor;
217
218         if (meshcache_read_mdd_range(fp, verts_tot, frame, interp,
219                                      index_range, &factor,  /* read into these values */
220                                      err_str) == false)
221         {
222                 return false;
223         }
224
225         if (index_range[0] == index_range[1]) {
226                 /* read single */
227                 if ((fseek(fp, 0, SEEK_SET) == 0) &&
228                     MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, err_str))
229                 {
230                         return true;
231                 }
232                 else {
233                         return false;
234                 }
235         }
236         else {
237                 /* read both and interpolate */
238                 if ((fseek(fp, 0, SEEK_SET) == 0) &&
239                     MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, err_str) &&
240                     (fseek(fp, 0, SEEK_SET) == 0) &&
241                     MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[1], factor, err_str))
242                 {
243                         return true;
244                 }
245                 else {
246                         return false;
247                 }
248         }
249 }
250
251 bool MOD_meshcache_read_mdd_times(const char *filepath,
252                                   float (*vertexCos)[3], const int verts_tot, const char interp,
253                                   const float time, const float fps, const char time_mode,
254                                   const char **err_str)
255 {
256         float frame;
257
258         FILE *fp = BLI_fopen(filepath, "rb");
259         bool ok;
260
261         if (fp == NULL) {
262                 *err_str = errno ? strerror(errno) : "Unknown error opening file";
263                 return false;
264         }
265
266         switch (time_mode) {
267                 case MOD_MESHCACHE_TIME_FRAME:
268                 {
269                         frame = time;
270                         break;
271                 }
272                 case MOD_MESHCACHE_TIME_SECONDS:
273                 {
274                         /* we need to find the closest time */
275                         if (meshcache_read_mdd_range_from_time(fp, verts_tot, time, fps, &frame, err_str) == false) {
276                                 fclose(fp);
277                                 return false;
278                         }
279                         rewind(fp);
280                         break;
281                 }
282                 case MOD_MESHCACHE_TIME_FACTOR:
283                 default:
284                 {
285                         MDDHead mdd_head;
286                         if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
287                                 fclose(fp);
288                                 return false;
289                         }
290
291                         frame = CLAMPIS(time, 0.0f, 1.0f) * (float)mdd_head.frame_tot;
292                         rewind(fp);
293                         break;
294                 }
295         }
296
297         ok = MOD_meshcache_read_mdd_frame(fp, vertexCos, verts_tot, interp, frame, err_str);
298
299         fclose(fp);
300         return ok;
301 }