Merge branch 'blender-v2.82-release'
[blender.git] / source / blender / modifiers / intern / MOD_meshcache_mdd.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup modifiers
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <errno.h>
24
25 #include "BLI_utildefines.h"
26
27 #include "BLI_fileops.h"
28 #include "BLI_math.h"
29 #ifdef __LITTLE_ENDIAN__
30 #  include "BLI_endian_switch.h"
31 #endif
32 #ifdef WIN32
33 #  include "BLI_winstuff.h"
34 #endif
35
36 #include "DNA_modifier_types.h"
37
38 #include "MOD_meshcache_util.h" /* own include */
39
40 typedef struct MDDHead {
41   int frame_tot;
42   int verts_tot;
43 } MDDHead; /* frames, verts */
44
45 static bool meshcache_read_mdd_head(FILE *fp,
46                                     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,
79                                      const char interp,
80                                      int r_index_range[2],
81                                      float *r_factor,
82                                      const char **err_str)
83 {
84   MDDHead mdd_head;
85
86   /* first check interpolation and get the vert locations */
87
88   if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
89     return false;
90   }
91
92   MOD_meshcache_calc_range(frame, interp, mdd_head.frame_tot, r_index_range, r_factor);
93
94   return true;
95 }
96
97 static bool meshcache_read_mdd_range_from_time(FILE *fp,
98                                                const int verts_tot,
99                                                const float time,
100                                                const float UNUSED(fps),
101                                                float *r_frame,
102                                                const char **err_str)
103 {
104   MDDHead mdd_head;
105   int i;
106   float f_time, f_time_prev = FLT_MAX;
107   float frame;
108
109   if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
110     return false;
111   }
112
113   for (i = 0; i < mdd_head.frame_tot; i++) {
114     fread(&f_time, sizeof(float), 1, fp);
115 #ifdef __LITTLE_ENDIAN__
116     BLI_endian_switch_float(&f_time);
117 #endif
118     if (f_time >= time) {
119       break;
120     }
121     f_time_prev = f_time;
122   }
123
124   if (i == mdd_head.frame_tot) {
125     frame = (float)(mdd_head.frame_tot - 1);
126   }
127   if (UNLIKELY(f_time_prev == FLT_MAX)) {
128     frame = 0.0f;
129   }
130   else {
131     const float range = f_time - f_time_prev;
132
133     if (range <= FRAME_SNAP_EPS) {
134       frame = (float)i;
135     }
136     else {
137       frame = (float)(i - 1) + ((time - f_time_prev) / range);
138     }
139   }
140
141   *r_frame = frame;
142   return true;
143 }
144
145 bool MOD_meshcache_read_mdd_index(FILE *fp,
146                                   float (*vertexCos)[3],
147                                   const int verts_tot,
148                                   const int index,
149                                   const float factor,
150                                   const char **err_str)
151 {
152   MDDHead mdd_head;
153
154   if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
155     return false;
156   }
157
158   if (fseek(fp, mdd_head.frame_tot * sizeof(int), SEEK_CUR) != 0) {
159     *err_str = "Header seek failed";
160     return false;
161   }
162
163   if (fseek(fp, sizeof(float) * 3 * index * mdd_head.verts_tot, SEEK_CUR) != 0) {
164     *err_str = "Failed to seek frame";
165     return false;
166   }
167
168   if (factor >= 1.0f) {
169 #if 1
170     float *vco = *vertexCos;
171     uint i;
172     for (i = mdd_head.verts_tot; i != 0; i--, vco += 3) {
173       fread(vco, sizeof(float) * 3, 1, fp);
174
175 #  ifdef __LITTLE_ENDIAN__
176       BLI_endian_switch_float(vco + 0);
177       BLI_endian_switch_float(vco + 1);
178       BLI_endian_switch_float(vco + 2);
179 #  endif /* __LITTLE_ENDIAN__ */
180     }
181 #else
182     /* no blending */
183     if (!fread(vertexCos, sizeof(float) * 3, mdd_head.verts_tot, f)) {
184       *err_str = errno ? strerror(errno) : "Failed to read frame";
185       return false;
186     }
187 #  ifdef __LITTLE_ENDIAN__
188     BLI_endian_switch_float_array(vertexCos[0], mdd_head.verts_tot * 3);
189 #  endif
190 #endif
191   }
192   else {
193     const float ifactor = 1.0f - factor;
194     float *vco = *vertexCos;
195     uint i;
196     for (i = mdd_head.verts_tot; i != 0; i--, vco += 3) {
197       float tvec[3];
198       fread(tvec, sizeof(float) * 3, 1, fp);
199
200 #ifdef __LITTLE_ENDIAN__
201       BLI_endian_switch_float(tvec + 0);
202       BLI_endian_switch_float(tvec + 1);
203       BLI_endian_switch_float(tvec + 2);
204 #endif
205
206       vco[0] = (vco[0] * ifactor) + (tvec[0] * factor);
207       vco[1] = (vco[1] * ifactor) + (tvec[1] * factor);
208       vco[2] = (vco[2] * ifactor) + (tvec[2] * factor);
209     }
210   }
211
212   return true;
213 }
214
215 bool MOD_meshcache_read_mdd_frame(FILE *fp,
216                                   float (*vertexCos)[3],
217                                   const int verts_tot,
218                                   const char interp,
219                                   const float frame,
220                                   const char **err_str)
221 {
222   int index_range[2];
223   float factor;
224
225   if (meshcache_read_mdd_range(fp,
226                                verts_tot,
227                                frame,
228                                interp,
229                                index_range,
230                                &factor, /* read into these values */
231                                err_str) == false) {
232     return false;
233   }
234
235   if (index_range[0] == index_range[1]) {
236     /* read single */
237     if ((fseek(fp, 0, SEEK_SET) == 0) &&
238         MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, err_str)) {
239       return true;
240     }
241     else {
242       return false;
243     }
244   }
245   else {
246     /* read both and interpolate */
247     if ((fseek(fp, 0, SEEK_SET) == 0) &&
248         MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, err_str) &&
249         (fseek(fp, 0, SEEK_SET) == 0) &&
250         MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[1], factor, err_str)) {
251       return true;
252     }
253     else {
254       return false;
255     }
256   }
257 }
258
259 bool MOD_meshcache_read_mdd_times(const char *filepath,
260                                   float (*vertexCos)[3],
261                                   const int verts_tot,
262                                   const char interp,
263                                   const float time,
264                                   const float fps,
265                                   const char time_mode,
266                                   const char **err_str)
267 {
268   float frame;
269
270   FILE *fp = BLI_fopen(filepath, "rb");
271   bool ok;
272
273   if (fp == NULL) {
274     *err_str = errno ? strerror(errno) : "Unknown error opening file";
275     return false;
276   }
277
278   switch (time_mode) {
279     case MOD_MESHCACHE_TIME_FRAME: {
280       frame = time;
281       break;
282     }
283     case MOD_MESHCACHE_TIME_SECONDS: {
284       /* we need to find the closest time */
285       if (meshcache_read_mdd_range_from_time(fp, verts_tot, time, fps, &frame, err_str) == false) {
286         fclose(fp);
287         return false;
288       }
289       rewind(fp);
290       break;
291     }
292     case MOD_MESHCACHE_TIME_FACTOR:
293     default: {
294       MDDHead mdd_head;
295       if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
296         fclose(fp);
297         return false;
298       }
299
300       frame = CLAMPIS(time, 0.0f, 1.0f) * (float)mdd_head.frame_tot;
301       rewind(fp);
302       break;
303     }
304   }
305
306   ok = MOD_meshcache_read_mdd_frame(fp, vertexCos, verts_tot, interp, frame, err_str);
307
308   fclose(fp);
309   return ok;
310 }