Blender  V2.93
MOD_meshcache_mdd.c
Go to the documentation of this file.
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 
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 
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 
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 
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  size_t num_frames_read = 0;
114  size_t num_frames_expect = mdd_head.frame_tot;
115  errno = 0;
116  for (i = 0; i < mdd_head.frame_tot; i++) {
117  num_frames_read += fread(&f_time, sizeof(float), 1, fp);
118 #ifdef __LITTLE_ENDIAN__
119  BLI_endian_switch_float(&f_time);
120 #endif
121  if (f_time >= time) {
122  num_frames_expect = i + 1;
123  break;
124  }
125  f_time_prev = f_time;
126  }
127 
128  if (num_frames_read != num_frames_expect) {
129  *err_str = errno ? strerror(errno) : "Timestamp read failed";
130  return false;
131  }
132 
133  if (i == mdd_head.frame_tot) {
134  frame = (float)(mdd_head.frame_tot - 1);
135  }
136  if (UNLIKELY(f_time_prev == FLT_MAX)) {
137  frame = 0.0f;
138  }
139  else {
140  const float range = f_time - f_time_prev;
141 
142  if (range <= FRAME_SNAP_EPS) {
143  frame = (float)i;
144  }
145  else {
146  frame = (float)(i - 1) + ((time - f_time_prev) / range);
147  }
148  }
149 
150  *r_frame = frame;
151  return true;
152 }
153 
155  float (*vertexCos)[3],
156  const int verts_tot,
157  const int index,
158  const float factor,
159  const char **err_str)
160 {
161  MDDHead mdd_head;
162 
163  if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
164  return false;
165  }
166 
167  if (BLI_fseek(fp, mdd_head.frame_tot * sizeof(int), SEEK_CUR) != 0) {
168  *err_str = "Header seek failed";
169  return false;
170  }
171 
172  if (BLI_fseek(fp, sizeof(float[3]) * index * mdd_head.verts_tot, SEEK_CUR) != 0) {
173  *err_str = "Failed to seek frame";
174  return false;
175  }
176 
177  size_t num_verts_read = 0;
178  errno = 0;
179  if (factor >= 1.0f) {
180 #if 1
181  float *vco = *vertexCos;
182  uint i;
183  for (i = mdd_head.verts_tot; i != 0; i--, vco += 3) {
184  num_verts_read += fread(vco, sizeof(float[3]), 1, fp);
185 
186 # ifdef __LITTLE_ENDIAN__
187  BLI_endian_switch_float(vco + 0);
188  BLI_endian_switch_float(vco + 1);
189  BLI_endian_switch_float(vco + 2);
190 # endif /* __LITTLE_ENDIAN__ */
191  }
192 #else
193  /* no blending */
194  if (!fread(vertexCos, sizeof(float[3]), mdd_head.verts_tot, f)) {
195  *err_str = errno ? strerror(errno) : "Failed to read frame";
196  return false;
197  }
198 # ifdef __LITTLE_ENDIAN__
199  BLI_endian_switch_float_array(vertexCos[0], mdd_head.verts_tot * 3);
200 # endif
201 #endif
202  }
203  else {
204  const float ifactor = 1.0f - factor;
205  float *vco = *vertexCos;
206  uint i;
207  for (i = mdd_head.verts_tot; i != 0; i--, vco += 3) {
208  float tvec[3];
209  num_verts_read += fread(tvec, sizeof(float[3]), 1, fp);
210 
211 #ifdef __LITTLE_ENDIAN__
212  BLI_endian_switch_float(tvec + 0);
213  BLI_endian_switch_float(tvec + 1);
214  BLI_endian_switch_float(tvec + 2);
215 #endif
216 
217  vco[0] = (vco[0] * ifactor) + (tvec[0] * factor);
218  vco[1] = (vco[1] * ifactor) + (tvec[1] * factor);
219  vco[2] = (vco[2] * ifactor) + (tvec[2] * factor);
220  }
221  }
222 
223  if (num_verts_read != mdd_head.verts_tot) {
224  *err_str = errno ? strerror(errno) : "Vertex coordinate read failed";
225  return false;
226  }
227 
228  return true;
229 }
230 
232  float (*vertexCos)[3],
233  const int verts_tot,
234  const char interp,
235  const float frame,
236  const char **err_str)
237 {
238  int index_range[2];
239  float factor;
240 
242  verts_tot,
243  frame,
244  interp,
245  index_range,
246  &factor, /* read into these values */
247  err_str) == false) {
248  return false;
249  }
250 
251  if (index_range[0] == index_range[1]) {
252  /* read single */
253  if ((BLI_fseek(fp, 0, SEEK_SET) == 0) &&
254  MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, err_str)) {
255  return true;
256  }
257 
258  return false;
259  }
260 
261  /* read both and interpolate */
262  if ((BLI_fseek(fp, 0, SEEK_SET) == 0) &&
263  MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, err_str) &&
264  (BLI_fseek(fp, 0, SEEK_SET) == 0) &&
265  MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[1], factor, err_str)) {
266  return true;
267  }
268 
269  return false;
270 }
271 
272 bool MOD_meshcache_read_mdd_times(const char *filepath,
273  float (*vertexCos)[3],
274  const int verts_tot,
275  const char interp,
276  const float time,
277  const float fps,
278  const char time_mode,
279  const char **err_str)
280 {
281  float frame;
282 
283  FILE *fp = BLI_fopen(filepath, "rb");
284  bool ok;
285 
286  if (fp == NULL) {
287  *err_str = errno ? strerror(errno) : "Unknown error opening file";
288  return false;
289  }
290 
291  switch (time_mode) {
293  frame = time;
294  break;
295  }
297  /* we need to find the closest time */
298  if (meshcache_read_mdd_range_from_time(fp, verts_tot, time, fps, &frame, err_str) == false) {
299  fclose(fp);
300  return false;
301  }
302  rewind(fp);
303  break;
304  }
306  default: {
307  MDDHead mdd_head;
308  if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
309  fclose(fp);
310  return false;
311  }
312 
313  frame = CLAMPIS(time, 0.0f, 1.0f) * (float)mdd_head.frame_tot;
314  rewind(fp);
315  break;
316  }
317  }
318 
319  ok = MOD_meshcache_read_mdd_frame(fp, vertexCos, verts_tot, interp, frame, err_str);
320 
321  fclose(fp);
322  return ok;
323 }
typedef float(TangentPoint)[2]
void BLI_endian_switch_int32_array(int *val, const int size) ATTR_NONNULL(1)
Definition: endian_switch.c:45
void BLI_endian_switch_float_array(float *val, const int size) ATTR_NONNULL(1)
Definition: endian_switch.c:65
BLI_INLINE void BLI_endian_switch_float(float *val) ATTR_NONNULL(1)
File and directory operations.
int BLI_fseek(FILE *stream, int64_t offset, int whence)
Definition: storage.c:186
FILE * BLI_fopen(const char *filename, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:1003
unsigned int uint
Definition: BLI_sys_types.h:83
#define CLAMPIS(a, b, c)
#define UNUSED(x)
#define UNLIKELY(x)
Compatibility-like things for windows.
@ MOD_MESHCACHE_TIME_FRAME
@ MOD_MESHCACHE_TIME_FACTOR
@ MOD_MESHCACHE_TIME_SECONDS
bool 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)
bool MOD_meshcache_read_mdd_frame(FILE *fp, float(*vertexCos)[3], const int verts_tot, const char interp, const float frame, const char **err_str)
static bool 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)
static bool 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)
bool MOD_meshcache_read_mdd_index(FILE *fp, float(*vertexCos)[3], const int verts_tot, const int index, const float factor, const char **err_str)
struct MDDHead MDDHead
static bool meshcache_read_mdd_head(FILE *fp, const int verts_tot, MDDHead *mdd_head, const char **err_str)
void MOD_meshcache_calc_range(const float frame, const char interp, const int frame_tot, int r_index_range[2], float *r_factor)
#define FRAME_SNAP_EPS
double time
ccl_device_inline float2 interp(const float2 &a, const float2 &b, float t)