Blender V5.0
MOD_meshcache_mdd.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cerrno>
11#include <cstdio>
12#include <cstring>
13
14#include "BLI_endian_switch.h"
15#include "BLI_fileops.h"
16#include "BLI_math_base.h"
17#ifdef WIN32
18# include "BLI_winstuff.h"
19#endif
20
21#include "BLT_translation.hh"
22
23#include "DNA_modifier_types.h"
24
25#include "MOD_meshcache_util.hh" /* own include */
26
27struct MDDHead {
30}; /* frames, verts */
31
32static bool meshcache_read_mdd_head(FILE *fp,
33 const int verts_tot,
34 MDDHead *mdd_head,
35 const char **r_err_str)
36{
37 if (!fread(mdd_head, sizeof(*mdd_head), 1, fp)) {
38 *r_err_str = RPT_("Missing header");
39 return false;
40 }
41
42 /* NOTE: this is endianness-sensitive. */
43 /* MDD is big-endian, its values need to be switched on little-endian systems. */
44 BLI_endian_switch_int32_array((int *)mdd_head, 2);
45
46 if (mdd_head->verts_tot != verts_tot) {
47 *r_err_str = RPT_("Vertex count mismatch");
48 return false;
49 }
50
51 if (mdd_head->frame_tot <= 0) {
52 *r_err_str = RPT_("Invalid frame total");
53 return false;
54 }
55 /* Intentionally don't seek back. */
56
57 return true;
58}
59
63static bool meshcache_read_mdd_range(FILE *fp,
64 const int verts_tot,
65 const float frame,
66 const char interp,
67 int r_index_range[2],
68 float *r_factor,
69 const char **r_err_str)
70{
71 MDDHead mdd_head;
72
73 /* first check interpolation and get the vert locations */
74
75 if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, r_err_str) == false) {
76 return false;
77 }
78
79 MOD_meshcache_calc_range(frame, interp, mdd_head.frame_tot, r_index_range, r_factor);
80
81 return true;
82}
83
85 const int verts_tot,
86 const float time,
87 const float /*fps*/,
88 float *r_frame,
89 const char **r_err_str)
90{
91 MDDHead mdd_head;
92 int i;
93 float f_time, f_time_prev = FLT_MAX;
94 float frame;
95
96 if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, r_err_str) == false) {
97 return false;
98 }
99
100 size_t frames_num_read = 0;
101 size_t frames_num_expect = mdd_head.frame_tot;
102 errno = 0;
103 for (i = 0; i < mdd_head.frame_tot; i++) {
104 frames_num_read += fread(&f_time, sizeof(float), 1, fp);
105 /* NOTE: this is endianness-sensitive. */
106 /* MDD is big-endian, its values need to be switched on little-endian systems. */
108
109 if (f_time >= time) {
110 frames_num_expect = i + 1;
111 break;
112 }
113 f_time_prev = f_time;
114 }
115
116 if (frames_num_read != frames_num_expect) {
117 *r_err_str = errno ? strerror(errno) : RPT_("Timestamp read failed");
118 return false;
119 }
120
121 if (UNLIKELY(f_time_prev == FLT_MAX)) {
122 frame = 0.0f;
123 }
124 else {
125 const float range = f_time - f_time_prev;
126
127 if (range <= FRAME_SNAP_EPS) {
128 frame = float(i);
129 }
130 else {
131 frame = float(i - 1) + ((time - f_time_prev) / range);
132 }
133 }
134
135 *r_frame = frame;
136 return true;
137}
138
140 float (*vertexCos)[3],
141 const int verts_tot,
142 const int index,
143 const float factor,
144 const char **r_err_str)
145{
146 MDDHead mdd_head;
147
148 if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, r_err_str) == false) {
149 return false;
150 }
151
152 if (BLI_fseek(fp, mdd_head.frame_tot * sizeof(int), SEEK_CUR) != 0) {
153 *r_err_str = RPT_("Header seek failed");
154 return false;
155 }
156
157 if (BLI_fseek(fp, sizeof(float[3]) * index * mdd_head.verts_tot, SEEK_CUR) != 0) {
158 *r_err_str = RPT_("Failed to seek frame");
159 return false;
160 }
161
162 size_t verts_read_num = 0;
163 errno = 0;
164 if (factor >= 1.0f) {
165#if 1
166 float *vco = *vertexCos;
167 uint i;
168 for (i = mdd_head.verts_tot; i != 0; i--, vco += 3) {
169 verts_read_num += fread(vco, sizeof(float[3]), 1, fp);
170
171 /* NOTE: this is endianness-sensitive. */
172 /* MDD is big-endian, its values need to be switched on little-endian systems. */
176 }
177#else
178 /* no blending */
179 if (!fread(vertexCos, sizeof(float[3]), mdd_head.verts_tot, f)) {
180 *r_err_str = errno ? strerror(errno) : RPT_("Failed to read frame");
181 return false;
182 }
183 /* NOTE: this is endianness-sensitive. */
184 /* MDD is big-endian, its values need to be switched on little-endian systems. */
185 BLI_endian_switch_float_array(vertexCos[0], mdd_head.verts_tot * 3);
186#endif
187 }
188 else {
189 const float ifactor = 1.0f - factor;
190 float *vco = *vertexCos;
191 uint i;
192 for (i = mdd_head.verts_tot; i != 0; i--, vco += 3) {
193 float tvec[3];
194 verts_read_num += fread(tvec, sizeof(float[3]), 1, fp);
195
196 /* NOTE: this is endianness-sensitive. */
197 /* MDD is big-endian, its values need to be switched on little-endian systems. */
198 BLI_endian_switch_float(tvec + 0);
199 BLI_endian_switch_float(tvec + 1);
200 BLI_endian_switch_float(tvec + 2);
201
202 vco[0] = (vco[0] * ifactor) + (tvec[0] * factor);
203 vco[1] = (vco[1] * ifactor) + (tvec[1] * factor);
204 vco[2] = (vco[2] * ifactor) + (tvec[2] * factor);
205 }
206 }
207
208 if (verts_read_num != mdd_head.verts_tot) {
209 *r_err_str = errno ? strerror(errno) : RPT_("Vertex coordinate read failed");
210 return false;
211 }
212
213 return true;
214}
215
217 float (*vertexCos)[3],
218 const int verts_tot,
219 const char interp,
220 const float frame,
221 const char **r_err_str)
222{
223 int index_range[2];
224 float factor;
225
227 verts_tot,
228 frame,
229 interp,
230 index_range,
231 &factor, /* read into these values */
232 r_err_str) == false)
233 {
234 return false;
235 }
236
237 if (index_range[0] == index_range[1]) {
238 /* read single */
239 if ((BLI_fseek(fp, 0, SEEK_SET) == 0) &&
240 MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, r_err_str))
241 {
242 return true;
243 }
244
245 return false;
246 }
247
248 /* read both and interpolate */
249 if ((BLI_fseek(fp, 0, SEEK_SET) == 0) &&
250 MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, r_err_str) &&
251 (BLI_fseek(fp, 0, SEEK_SET) == 0) &&
252 MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[1], factor, r_err_str))
253 {
254 return true;
255 }
256
257 return false;
258}
259
260bool MOD_meshcache_read_mdd_times(const char *filepath,
261 float (*vertexCos)[3],
262 const int verts_tot,
263 const char interp,
264 const float time,
265 const float fps,
266 const char time_mode,
267 const char **r_err_str)
268{
269 float frame;
270
271 FILE *fp = BLI_fopen(filepath, "rb");
272 bool ok;
273
274 if (fp == nullptr) {
275 *r_err_str = errno ? strerror(errno) : RPT_("Unknown error opening file");
276 return false;
277 }
278
279 switch (time_mode) {
281 frame = time;
282 break;
283 }
285 /* we need to find the closest time */
286 if (meshcache_read_mdd_range_from_time(fp, verts_tot, time, fps, &frame, r_err_str) == false)
287 {
288 fclose(fp);
289 return false;
290 }
291 rewind(fp);
292 break;
293 }
295 default: {
296 MDDHead mdd_head;
297 if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, r_err_str) == false) {
298 fclose(fp);
299 return false;
300 }
301
302 frame = std::clamp(time, 0.0f, 1.0f) * float(mdd_head.frame_tot);
303 rewind(fp);
304 break;
305 }
306 }
307
308 ok = MOD_meshcache_read_mdd_frame(fp, vertexCos, verts_tot, interp, frame, r_err_str);
309
310 fclose(fp);
311 return ok;
312}
void BLI_endian_switch_float_array(float *val, int size) ATTR_NONNULL(1)
void BLI_endian_switch_int32_array(int *val, int size) ATTR_NONNULL(1)
BLI_INLINE void BLI_endian_switch_float(float *val) ATTR_NONNULL(1)
File and directory operations.
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int BLI_fseek(FILE *stream, int64_t offset, int whence)
Definition storage.cc:199
unsigned int uint
#define UNLIKELY(x)
Compatibility-like things for windows.
#define RPT_(msgid)
@ MOD_MESHCACHE_TIME_FRAME
@ MOD_MESHCACHE_TIME_FACTOR
@ MOD_MESHCACHE_TIME_SECONDS
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 **r_err_str)
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 **r_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 **r_err_str)
static bool meshcache_read_mdd_range_from_time(FILE *fp, const int verts_tot, const float time, const float, float *r_frame, const char **r_err_str)
static bool meshcache_read_mdd_head(FILE *fp, const int verts_tot, MDDHead *mdd_head, const char **r_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 **r_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
nullptr float
ccl_device_inline float interp(const float a, const float b, const float t)
Definition math_base.h:502
#define FLT_MAX
Definition stdcycles.h:14
i
Definition text_draw.cc:230