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