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 <errno.h>
22 #include <stdio.h>
23 #include <string.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 
meshcache_read_mdd_head(FILE * fp,const int verts_tot,MDDHead * mdd_head,const char ** err_str)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 don't seek back. */
69 
70   return true;
71 }
72 
73 /**
74  * Gets the index range and factor.
75  */
meshcache_read_mdd_range(FILE * fp,const int verts_tot,const float frame,const char interp,int r_index_range[2],float * r_factor,const char ** err_str)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 
meshcache_read_mdd_range_from_time(FILE * fp,const int verts_tot,const float time,const float UNUSED (fps),float * r_frame,const char ** err_str)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 
MOD_meshcache_read_mdd_index(FILE * fp,float (* vertexCos)[3],const int verts_tot,const int index,const float factor,const char ** err_str)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 (BLI_fseek(fp, mdd_head.frame_tot * sizeof(int), SEEK_CUR) != 0) {
159     *err_str = "Header seek failed";
160     return false;
161   }
162 
163   if (BLI_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 
MOD_meshcache_read_mdd_frame(FILE * fp,float (* vertexCos)[3],const int verts_tot,const char interp,const float frame,const char ** err_str)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 ((BLI_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 
242     return false;
243   }
244 
245   /* read both and interpolate */
246   if ((BLI_fseek(fp, 0, SEEK_SET) == 0) &&
247       MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, err_str) &&
248       (BLI_fseek(fp, 0, SEEK_SET) == 0) &&
249       MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[1], factor, err_str)) {
250     return true;
251   }
252 
253   return false;
254 }
255 
MOD_meshcache_read_mdd_times(const char * filepath,float (* vertexCos)[3],const int verts_tot,const char interp,const float time,const float fps,const char time_mode,const char ** err_str)256 bool MOD_meshcache_read_mdd_times(const char *filepath,
257                                   float (*vertexCos)[3],
258                                   const int verts_tot,
259                                   const char interp,
260                                   const float time,
261                                   const float fps,
262                                   const char time_mode,
263                                   const char **err_str)
264 {
265   float frame;
266 
267   FILE *fp = BLI_fopen(filepath, "rb");
268   bool ok;
269 
270   if (fp == NULL) {
271     *err_str = errno ? strerror(errno) : "Unknown error opening file";
272     return false;
273   }
274 
275   switch (time_mode) {
276     case MOD_MESHCACHE_TIME_FRAME: {
277       frame = time;
278       break;
279     }
280     case MOD_MESHCACHE_TIME_SECONDS: {
281       /* we need to find the closest time */
282       if (meshcache_read_mdd_range_from_time(fp, verts_tot, time, fps, &frame, err_str) == false) {
283         fclose(fp);
284         return false;
285       }
286       rewind(fp);
287       break;
288     }
289     case MOD_MESHCACHE_TIME_FACTOR:
290     default: {
291       MDDHead mdd_head;
292       if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
293         fclose(fp);
294         return false;
295       }
296 
297       frame = CLAMPIS(time, 0.0f, 1.0f) * (float)mdd_head.frame_tot;
298       rewind(fp);
299       break;
300     }
301   }
302 
303   ok = MOD_meshcache_read_mdd_frame(fp, vertexCos, verts_tot, interp, frame, err_str);
304 
305   fclose(fp);
306   return ok;
307 }
308