Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* m3d.h |
3 |
|
|
* https://gitlab.com/bztsrc/model3d |
4 |
|
|
* |
5 |
|
|
* Copyright (C) 2020 bzt (bztsrc@gitlab) |
6 |
|
|
* |
7 |
|
|
* Permission is hereby granted, free of charge, to any person |
8 |
|
|
* obtaining a copy of this software and associated documentation |
9 |
|
|
* files (the "Software"), to deal in the Software without |
10 |
|
|
* restriction, including without limitation the rights to use, copy, |
11 |
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies |
12 |
|
|
* of the Software, and to permit persons to whom the Software is |
13 |
|
|
* furnished to do so, subject to the following conditions: |
14 |
|
|
* |
15 |
|
|
* The above copyright notice and this permission notice shall be |
16 |
|
|
* included in all copies or substantial portions of the Software. |
17 |
|
|
* |
18 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
19 |
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
20 |
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
21 |
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
22 |
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
23 |
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 |
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
25 |
|
|
* DEALINGS IN THE SOFTWARE. |
26 |
|
|
* |
27 |
|
|
* @brief ANSI C89 / C++11 single header importer / exporter SDK for the Model 3D (.M3D) format |
28 |
|
|
* https://gitlab.com/bztsrc/model3d |
29 |
|
|
* |
30 |
|
|
* PNG decompressor included from (with minor modifications to make it C89 valid): |
31 |
|
|
* stb_image - v2.13 - public domain image loader - http://nothings.org/stb_image.h |
32 |
|
|
* |
33 |
|
|
* @version: 1.0.0 |
34 |
|
|
*/ |
35 |
|
|
|
36 |
|
|
#ifndef _M3D_H_ |
37 |
|
|
#define _M3D_H_ |
38 |
|
|
|
39 |
|
|
#ifdef __cplusplus |
40 |
|
|
extern "C" { |
41 |
|
|
#endif |
42 |
|
|
|
43 |
|
|
#include <stdint.h> |
44 |
|
|
|
45 |
|
|
/*** configuration ***/ |
46 |
|
|
#ifndef M3D_MALLOC |
47 |
|
|
# define M3D_MALLOC(sz) malloc(sz) |
48 |
|
|
#endif |
49 |
|
|
#ifndef M3D_REALLOC |
50 |
|
|
# define M3D_REALLOC(p,nsz) realloc(p,nsz) |
51 |
|
|
#endif |
52 |
|
|
#ifndef M3D_FREE |
53 |
|
|
# define M3D_FREE(p) free(p) |
54 |
|
|
#endif |
55 |
|
|
#ifndef M3D_LOG |
56 |
|
|
# define M3D_LOG(x) |
57 |
|
|
#endif |
58 |
|
|
#ifndef M3D_APIVERSION |
59 |
|
|
#define M3D_APIVERSION 0x0100 |
60 |
|
|
#ifndef M3D_DOUBLE |
61 |
|
|
typedef float M3D_FLOAT; |
62 |
|
|
#ifndef M3D_EPSILON |
63 |
|
|
/* carefully choosen for IEEE 754 don't change */ |
64 |
|
|
#define M3D_EPSILON ((M3D_FLOAT)1e-7) |
65 |
|
|
#endif |
66 |
|
|
#else |
67 |
|
|
typedef double M3D_FLOAT; |
68 |
|
|
#ifndef M3D_EPSILON |
69 |
|
|
#define M3D_EPSILON ((M3D_FLOAT)1e-14) |
70 |
|
|
#endif |
71 |
|
|
#endif |
72 |
|
|
#if !defined(M3D_SMALLINDEX) |
73 |
|
|
typedef uint32_t M3D_INDEX; |
74 |
|
|
typedef uint16_t M3D_VOXEL; |
75 |
|
|
#define M3D_UNDEF 0xffffffff |
76 |
|
|
#define M3D_INDEXMAX 0xfffffffe |
77 |
|
|
#define M3D_VOXUNDEF 0xffff |
78 |
|
|
#define M3D_VOXCLEAR 0xfffe |
79 |
|
|
#else |
80 |
|
|
typedef uint16_t M3D_INDEX; |
81 |
|
|
typedef uint8_t M3D_VOXEL; |
82 |
|
|
#define M3D_UNDEF 0xffff |
83 |
|
|
#define M3D_INDEXMAX 0xfffe |
84 |
|
|
#define M3D_VOXUNDEF 0xff |
85 |
|
|
#define M3D_VOXCLEAR 0xfe |
86 |
|
|
#endif |
87 |
|
|
#define M3D_NOTDEFINED 0xffffffff |
88 |
|
|
#ifndef M3D_NUMBONE |
89 |
|
|
#define M3D_NUMBONE 4 |
90 |
|
|
#endif |
91 |
|
|
#ifndef M3D_BONEMAXLEVEL |
92 |
|
|
#define M3D_BONEMAXLEVEL 8 |
93 |
|
|
#endif |
94 |
|
|
#ifndef _MSC_VER |
95 |
|
|
#ifndef _inline |
96 |
|
|
#define _inline __inline__ |
97 |
|
|
#endif |
98 |
|
|
#define _pack __attribute__((packed)) |
99 |
|
|
#define _unused __attribute__((unused)) |
100 |
|
|
#else |
101 |
|
|
#define _inline |
102 |
|
|
#define _pack |
103 |
|
|
#define _unused __pragma(warning(suppress:4100)) |
104 |
|
|
#endif |
105 |
|
|
#ifndef __cplusplus |
106 |
|
|
#define _register register |
107 |
|
|
#else |
108 |
|
|
#define _register |
109 |
|
|
#endif |
110 |
|
|
|
111 |
|
|
/*** File format structures ***/ |
112 |
|
|
|
113 |
|
|
/** |
114 |
|
|
* M3D file format structure |
115 |
|
|
* 3DMO m3dchunk_t file header chunk, may followed by compressed data |
116 |
|
|
* PRVW preview chunk (optional) |
117 |
|
|
* HEAD m3dhdr_t model header chunk |
118 |
|
|
* n x m3dchunk_t more chunks follow |
119 |
|
|
* CMAP color map chunk (optional) |
120 |
|
|
* TMAP texture map chunk (optional) |
121 |
|
|
* VRTS vertex data chunk (optional if it's a material library) |
122 |
|
|
* BONE bind-pose skeleton, bone hierarchy chunk (optional) |
123 |
|
|
* n x m3db_t contains propably more, but at least one bone |
124 |
|
|
* n x m3ds_t skin group records |
125 |
|
|
* MTRL* material chunk(s), can be more (optional) |
126 |
|
|
* n x m3dp_t each material contains propapbly more, but at least one property |
127 |
|
|
* the properties are configurable with a static array, see m3d_propertytypes |
128 |
|
|
* n x m3dchunk_t at least one, but maybe more face chunks |
129 |
|
|
* PROC* procedural face, or |
130 |
|
|
* MESH* triangle mesh (vertex index list) or |
131 |
|
|
* VOXT, VOXD* voxel image (converted to mesh) or |
132 |
|
|
* SHPE* mathematical shapes like parameterized surfaces |
133 |
|
|
* LBLS* annotation label chunks, can be more (optional) |
134 |
|
|
* ACTN* action chunk(s), animation-pose skeletons, can be more (optional) |
135 |
|
|
* n x m3dfr_t each action contains probably more, but at least one frame |
136 |
|
|
* n x m3dtr_t each frame contains probably more, but at least one transformation |
137 |
|
|
* ASET* inlined asset chunk(s), can be more (optional) |
138 |
|
|
* OMD3 end chunk |
139 |
|
|
* |
140 |
|
|
* Typical chunks for a game engine: 3DMO, HEAD, CMAP, TMAP, VRTS, BONE, MTRL, MESH, ACTN, OMD3 |
141 |
|
|
* Typical chunks for distibution: 3DMO, PRVW, HEAD, CMAP, TMAP, VRTS, BONE, MTRL, MESH, ACTN, ASET, OMD3 |
142 |
|
|
* Typical chunks for voxel image: 3DMO, HEAD, CMAP, MTRL, VOXT, VOXD, VOXD, VOXD, OMD3 |
143 |
|
|
* Typical chunks for CAD software: 3DMO, PRVW, HEAD, CMAP, TMAP, VRTS, MTRL, SHPE, LBLS, OMD3 |
144 |
|
|
*/ |
145 |
|
|
#ifdef _MSC_VER |
146 |
|
|
#pragma pack(push) |
147 |
|
|
#pragma pack(1) |
148 |
|
|
#endif |
149 |
|
|
|
150 |
|
|
typedef struct { |
151 |
|
|
char magic[4]; |
152 |
|
|
uint32_t length; |
153 |
|
|
float scale; /* deliberately not M3D_FLOAT */ |
154 |
|
|
uint32_t types; |
155 |
|
|
} _pack m3dhdr_t; |
156 |
|
|
|
157 |
|
|
typedef struct { |
158 |
|
|
char magic[4]; |
159 |
|
|
uint32_t length; |
160 |
|
|
} _pack m3dchunk_t; |
161 |
|
|
|
162 |
|
|
#ifdef _MSC_VER |
163 |
|
|
#pragma pack(pop) |
164 |
|
|
#endif |
165 |
|
|
|
166 |
|
|
/*** in-memory model structure ***/ |
167 |
|
|
|
168 |
|
|
/* textmap entry */ |
169 |
|
|
typedef struct { |
170 |
|
|
M3D_FLOAT u; |
171 |
|
|
M3D_FLOAT v; |
172 |
|
|
} m3dti_t; |
173 |
|
|
#define m3d_textureindex_t m3dti_t |
174 |
|
|
|
175 |
|
|
/* texture */ |
176 |
|
|
typedef struct { |
177 |
|
|
char *name; /* texture name */ |
178 |
|
|
uint8_t *d; /* pixels data */ |
179 |
|
|
uint16_t w; /* width */ |
180 |
|
|
uint16_t h; /* height */ |
181 |
|
|
uint8_t f; /* format, 1 = grayscale, 2 = grayscale+alpha, 3 = rgb, 4 = rgba */ |
182 |
|
|
} m3dtx_t; |
183 |
|
|
#define m3d_texturedata_t m3dtx_t |
184 |
|
|
|
185 |
|
|
typedef struct { |
186 |
|
|
M3D_INDEX vertexid; |
187 |
|
|
M3D_FLOAT weight; |
188 |
|
|
} m3dw_t; |
189 |
|
|
#define m3d_weight_t m3dw_t |
190 |
|
|
|
191 |
|
|
/* bone entry */ |
192 |
|
|
typedef struct { |
193 |
|
|
M3D_INDEX parent; /* parent bone index */ |
194 |
|
|
char *name; /* name for this bone */ |
195 |
|
|
M3D_INDEX pos; /* vertex index position */ |
196 |
|
|
M3D_INDEX ori; /* vertex index orientation (quaternion) */ |
197 |
|
|
M3D_INDEX numweight; /* number of controlled vertices */ |
198 |
|
|
m3dw_t *weight; /* weights for those vertices */ |
199 |
|
|
M3D_FLOAT mat4[16]; /* transformation matrix */ |
200 |
|
|
} m3db_t; |
201 |
|
|
#define m3d_bone_t m3db_t |
202 |
|
|
|
203 |
|
|
/* skin: bone per vertex entry */ |
204 |
|
|
typedef struct { |
205 |
|
|
M3D_INDEX boneid[M3D_NUMBONE]; |
206 |
|
|
M3D_FLOAT weight[M3D_NUMBONE]; |
207 |
|
|
} m3ds_t; |
208 |
|
|
#define m3d_skin_t m3ds_t |
209 |
|
|
|
210 |
|
|
/* vertex entry */ |
211 |
|
|
typedef struct { |
212 |
|
|
M3D_FLOAT x; /* 3D coordinates and weight */ |
213 |
|
|
M3D_FLOAT y; |
214 |
|
|
M3D_FLOAT z; |
215 |
|
|
M3D_FLOAT w; |
216 |
|
|
uint32_t color; /* default vertex color */ |
217 |
|
|
M3D_INDEX skinid; /* skin index */ |
218 |
|
|
#ifdef M3D_VERTEXTYPE |
219 |
|
|
uint8_t type; |
220 |
|
|
#endif |
221 |
|
|
} m3dv_t; |
222 |
|
|
#define m3d_vertex_t m3dv_t |
223 |
|
|
|
224 |
|
|
/* material property formats */ |
225 |
|
|
enum { |
226 |
|
|
m3dpf_color, |
227 |
|
|
m3dpf_uint8, |
228 |
|
|
m3dpf_uint16, |
229 |
|
|
m3dpf_uint32, |
230 |
|
|
m3dpf_float, |
231 |
|
|
m3dpf_map |
232 |
|
|
}; |
233 |
|
|
typedef struct { |
234 |
|
|
uint8_t format; |
235 |
|
|
uint8_t id; |
236 |
|
|
#ifdef M3D_ASCII |
237 |
|
|
#define M3D_PROPERTYDEF(f,i,n) { (f), (i), (char*)(n) } |
238 |
|
|
char *key; |
239 |
|
|
#else |
240 |
|
|
#define M3D_PROPERTYDEF(f,i,n) { (f), (i) } |
241 |
|
|
#endif |
242 |
|
|
} m3dpd_t; |
243 |
|
|
|
244 |
|
|
/* material property types */ |
245 |
|
|
/* You shouldn't change the first 8 display and first 4 physical property. Assign the rest as you like. */ |
246 |
|
|
enum { |
247 |
|
|
m3dp_Kd = 0, /* scalar display properties */ |
248 |
|
|
m3dp_Ka, |
249 |
|
|
m3dp_Ks, |
250 |
|
|
m3dp_Ns, |
251 |
|
|
m3dp_Ke, |
252 |
|
|
m3dp_Tf, |
253 |
|
|
m3dp_Km, |
254 |
|
|
m3dp_d, |
255 |
|
|
m3dp_il, |
256 |
|
|
|
257 |
|
|
m3dp_Pr = 64, /* scalar physical properties */ |
258 |
|
|
m3dp_Pm, |
259 |
|
|
m3dp_Ps, |
260 |
|
|
m3dp_Ni, |
261 |
|
|
m3dp_Nt, |
262 |
|
|
|
263 |
|
|
m3dp_map_Kd = 128, /* textured display map properties */ |
264 |
|
|
m3dp_map_Ka, |
265 |
|
|
m3dp_map_Ks, |
266 |
|
|
m3dp_map_Ns, |
267 |
|
|
m3dp_map_Ke, |
268 |
|
|
m3dp_map_Tf, |
269 |
|
|
m3dp_map_Km, /* bump map */ |
270 |
|
|
m3dp_map_D, |
271 |
|
|
m3dp_map_N, /* normal map */ |
272 |
|
|
|
273 |
|
|
m3dp_map_Pr = 192, /* textured physical map properties */ |
274 |
|
|
m3dp_map_Pm, |
275 |
|
|
m3dp_map_Ps, |
276 |
|
|
m3dp_map_Ni, |
277 |
|
|
m3dp_map_Nt |
278 |
|
|
}; |
279 |
|
|
enum { /* aliases */ |
280 |
|
|
m3dp_bump = m3dp_map_Km, |
281 |
|
|
m3dp_map_il = m3dp_map_N, |
282 |
|
|
m3dp_refl = m3dp_map_Pm |
283 |
|
|
}; |
284 |
|
|
|
285 |
|
|
/* material property */ |
286 |
|
|
typedef struct { |
287 |
|
|
uint8_t type; /* property type, see "m3dp_*" enumeration */ |
288 |
|
|
union { |
289 |
|
|
uint32_t color; /* if value is a color, m3dpf_color */ |
290 |
|
|
uint32_t num; /* if value is a number, m3dpf_uint8, m3pf_uint16, m3dpf_uint32 */ |
291 |
|
|
float fnum; /* if value is a floating point number, m3dpf_float */ |
292 |
|
|
M3D_INDEX textureid; /* if value is a texture, m3dpf_map */ |
293 |
|
|
} value; |
294 |
|
|
} m3dp_t; |
295 |
|
|
#define m3d_property_t m3dp_t |
296 |
|
|
|
297 |
|
|
/* material entry */ |
298 |
|
|
typedef struct { |
299 |
|
|
char *name; /* name of the material */ |
300 |
|
|
uint8_t numprop; /* number of properties */ |
301 |
|
|
m3dp_t *prop; /* properties array */ |
302 |
|
|
} m3dm_t; |
303 |
|
|
#define m3d_material_t m3dm_t |
304 |
|
|
|
305 |
|
|
/* face entry */ |
306 |
|
|
typedef struct { |
307 |
|
|
M3D_INDEX materialid; /* material index */ |
308 |
|
|
M3D_INDEX vertex[3]; /* 3D points of the triangle in CCW order */ |
309 |
|
|
M3D_INDEX normal[3]; /* normal vectors */ |
310 |
|
|
M3D_INDEX texcoord[3]; /* UV coordinates */ |
311 |
|
|
#ifdef M3D_VERTEXMAX |
312 |
|
|
M3D_INDEX paramid; /* parameter index */ |
313 |
|
|
M3D_INDEX vertmax[3]; /* maximum 3D points of the triangle in CCW order */ |
314 |
|
|
#endif |
315 |
|
|
} m3df_t; |
316 |
|
|
#define m3d_face_t m3df_t |
317 |
|
|
|
318 |
|
|
typedef struct { |
319 |
|
|
uint16_t count; |
320 |
|
|
char *name; |
321 |
|
|
} m3dvi_t; |
322 |
|
|
#define m3d_voxelitem_t m3dvi_t |
323 |
|
|
#define m3d_parameter_t m3dvi_t |
324 |
|
|
|
325 |
|
|
/* voxel types (voxel palette) */ |
326 |
|
|
typedef struct { |
327 |
|
|
char *name; /* technical name of the voxel */ |
328 |
|
|
uint8_t rotation; /* rotation info */ |
329 |
|
|
uint16_t voxshape; /* voxel shape */ |
330 |
|
|
M3D_INDEX materialid; /* material index */ |
331 |
|
|
uint32_t color; /* default voxel color */ |
332 |
|
|
M3D_INDEX skinid; /* skin index */ |
333 |
|
|
uint8_t numitem; /* number of sub-voxels */ |
334 |
|
|
m3dvi_t *item; /* list of sub-voxels */ |
335 |
|
|
} m3dvt_t; |
336 |
|
|
#define m3d_voxeltype_t m3dvt_t |
337 |
|
|
|
338 |
|
|
/* voxel data blocks */ |
339 |
|
|
typedef struct { |
340 |
|
|
char *name; /* name of the block */ |
341 |
|
|
int32_t x, y, z; /* position */ |
342 |
|
|
uint32_t w, h, d; /* dimension */ |
343 |
|
|
uint8_t uncertain; /* probability */ |
344 |
|
|
uint8_t groupid; /* block group id */ |
345 |
|
|
M3D_VOXEL *data; /* voxel data, indices to voxel type */ |
346 |
|
|
} m3dvx_t; |
347 |
|
|
#define m3d_voxel_t m3dvx_t |
348 |
|
|
|
349 |
|
|
/* shape command types. must match the row in m3d_commandtypes */ |
350 |
|
|
enum { |
351 |
|
|
/* special commands */ |
352 |
|
|
m3dc_use = 0, /* use material */ |
353 |
|
|
m3dc_inc, /* include another shape */ |
354 |
|
|
m3dc_mesh, /* include part of polygon mesh */ |
355 |
|
|
/* approximations */ |
356 |
|
|
m3dc_div, /* subdivision by constant resolution for both u, v */ |
357 |
|
|
m3dc_sub, /* subdivision by constant, different for u and v */ |
358 |
|
|
m3dc_len, /* spacial subdivision by maxlength */ |
359 |
|
|
m3dc_dist, /* subdivision by maxdistance and maxangle */ |
360 |
|
|
/* modifiers */ |
361 |
|
|
m3dc_degu, /* degree for both u, v */ |
362 |
|
|
m3dc_deg, /* separate degree for u and v */ |
363 |
|
|
m3dc_rangeu, /* range for u */ |
364 |
|
|
m3dc_range, /* range for u and v */ |
365 |
|
|
m3dc_paru, /* u parameters (knots) */ |
366 |
|
|
m3dc_parv, /* v parameters */ |
367 |
|
|
m3dc_trim, /* outer trimming curve */ |
368 |
|
|
m3dc_hole, /* inner trimming curve */ |
369 |
|
|
m3dc_scrv, /* spacial curve */ |
370 |
|
|
m3dc_sp, /* special points */ |
371 |
|
|
/* helper curves */ |
372 |
|
|
m3dc_bez1, /* Bezier 1D */ |
373 |
|
|
m3dc_bsp1, /* B-spline 1D */ |
374 |
|
|
m3dc_bez2, /* bezier 2D */ |
375 |
|
|
m3dc_bsp2, /* B-spline 2D */ |
376 |
|
|
/* surfaces */ |
377 |
|
|
m3dc_bezun, /* Bezier 3D with control, UV, normal */ |
378 |
|
|
m3dc_bezu, /* with control and UV */ |
379 |
|
|
m3dc_bezn, /* with control and normal */ |
380 |
|
|
m3dc_bez, /* control points only */ |
381 |
|
|
m3dc_nurbsun, /* B-spline 3D */ |
382 |
|
|
m3dc_nurbsu, |
383 |
|
|
m3dc_nurbsn, |
384 |
|
|
m3dc_nurbs, |
385 |
|
|
m3dc_conn, /* connect surfaces */ |
386 |
|
|
/* geometrical */ |
387 |
|
|
m3dc_line, |
388 |
|
|
m3dc_polygon, |
389 |
|
|
m3dc_circle, |
390 |
|
|
m3dc_cylinder, |
391 |
|
|
m3dc_shpere, |
392 |
|
|
m3dc_torus, |
393 |
|
|
m3dc_cone, |
394 |
|
|
m3dc_cube |
395 |
|
|
}; |
396 |
|
|
|
397 |
|
|
/* shape command argument types */ |
398 |
|
|
enum { |
399 |
|
|
m3dcp_mi_t = 1, /* material index */ |
400 |
|
|
m3dcp_hi_t, /* shape index */ |
401 |
|
|
m3dcp_fi_t, /* face index */ |
402 |
|
|
m3dcp_ti_t, /* texture map index */ |
403 |
|
|
m3dcp_vi_t, /* vertex index */ |
404 |
|
|
m3dcp_qi_t, /* vertex index for quaternions */ |
405 |
|
|
m3dcp_vc_t, /* coordinate or radius, float scalar */ |
406 |
|
|
m3dcp_i1_t, /* int8 scalar */ |
407 |
|
|
m3dcp_i2_t, /* int16 scalar */ |
408 |
|
|
m3dcp_i4_t, /* int32 scalar */ |
409 |
|
|
m3dcp_va_t /* variadic arguments */ |
410 |
|
|
}; |
411 |
|
|
|
412 |
|
|
#define M3D_CMDMAXARG 8 /* if you increase this, add more arguments to the macro below */ |
413 |
|
|
typedef struct { |
414 |
|
|
#ifdef M3D_ASCII |
415 |
|
|
#define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (char*)(n), (p), { (a), (b), (c), (d), (e), (f), (g), (h) } } |
416 |
|
|
char *key; |
417 |
|
|
#else |
418 |
|
|
#define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (p), { (a), (b), (c), (d), (e), (f), (g), (h) } } |
419 |
|
|
#endif |
420 |
|
|
uint8_t p; |
421 |
|
|
uint8_t a[M3D_CMDMAXARG]; |
422 |
|
|
} m3dcd_t; |
423 |
|
|
|
424 |
|
|
/* shape command */ |
425 |
|
|
typedef struct { |
426 |
|
|
uint16_t type; /* shape type */ |
427 |
|
|
uint32_t *arg; /* arguments array */ |
428 |
|
|
} m3dc_t; |
429 |
|
|
#define m3d_shapecommand_t m3dc_t |
430 |
|
|
|
431 |
|
|
/* shape entry */ |
432 |
|
|
typedef struct { |
433 |
|
|
char *name; /* name of the mathematical shape */ |
434 |
|
|
M3D_INDEX group; /* group this shape belongs to or -1 */ |
435 |
|
|
uint32_t numcmd; /* number of commands */ |
436 |
|
|
m3dc_t *cmd; /* commands array */ |
437 |
|
|
} m3dh_t; |
438 |
|
|
#define m3d_shape_t m3dh_t |
439 |
|
|
|
440 |
|
|
/* label entry */ |
441 |
|
|
typedef struct { |
442 |
|
|
char *name; /* name of the annotation layer or NULL */ |
443 |
|
|
char *lang; /* language code or NULL */ |
444 |
|
|
char *text; /* the label text */ |
445 |
|
|
uint32_t color; /* color */ |
446 |
|
|
M3D_INDEX vertexid; /* the vertex the label refers to */ |
447 |
|
|
} m3dl_t; |
448 |
|
|
#define m3d_label_t m3dl_t |
449 |
|
|
|
450 |
|
|
/* frame transformations / working copy skeleton entry */ |
451 |
|
|
typedef struct { |
452 |
|
|
M3D_INDEX boneid; /* selects a node in bone hierarchy */ |
453 |
|
|
M3D_INDEX pos; /* vertex index new position */ |
454 |
|
|
M3D_INDEX ori; /* vertex index new orientation (quaternion) */ |
455 |
|
|
} m3dtr_t; |
456 |
|
|
#define m3d_transform_t m3dtr_t |
457 |
|
|
|
458 |
|
|
/* animation frame entry */ |
459 |
|
|
typedef struct { |
460 |
|
|
uint32_t msec; /* frame's position on the timeline, timestamp */ |
461 |
|
|
M3D_INDEX numtransform; /* number of transformations in this frame */ |
462 |
|
|
m3dtr_t *transform; /* transformations */ |
463 |
|
|
} m3dfr_t; |
464 |
|
|
#define m3d_frame_t m3dfr_t |
465 |
|
|
|
466 |
|
|
/* model action entry */ |
467 |
|
|
typedef struct { |
468 |
|
|
char *name; /* name of the action */ |
469 |
|
|
uint32_t durationmsec; /* duration in millisec (1/1000 sec) */ |
470 |
|
|
M3D_INDEX numframe; /* number of frames in this animation */ |
471 |
|
|
m3dfr_t *frame; /* frames array */ |
472 |
|
|
} m3da_t; |
473 |
|
|
#define m3d_action_t m3da_t |
474 |
|
|
|
475 |
|
|
/* inlined asset */ |
476 |
|
|
typedef struct { |
477 |
|
|
char *name; /* asset name (same pointer as in texture[].name) */ |
478 |
|
|
uint8_t *data; /* compressed asset data */ |
479 |
|
|
uint32_t length; /* compressed data length */ |
480 |
|
|
} m3di_t; |
481 |
|
|
#define m3d_inlinedasset_t m3di_t |
482 |
|
|
|
483 |
|
|
/*** in-memory model structure ***/ |
484 |
|
|
#define M3D_FLG_FREERAW (1<<0) |
485 |
|
|
#define M3D_FLG_FREESTR (1<<1) |
486 |
|
|
#define M3D_FLG_MTLLIB (1<<2) |
487 |
|
|
#define M3D_FLG_GENNORM (1<<3) |
488 |
|
|
|
489 |
|
|
typedef struct { |
490 |
|
|
m3dhdr_t *raw; /* pointer to raw data */ |
491 |
|
|
char flags; /* internal flags */ |
492 |
|
|
signed char errcode; /* returned error code */ |
493 |
|
|
char vc_s, vi_s, si_s, ci_s, ti_s, bi_s, nb_s, sk_s, fc_s, hi_s, fi_s, vd_s, vp_s; /* decoded sizes for types */ |
494 |
|
|
char *name; /* name of the model, like "Utah teapot" */ |
495 |
|
|
char *license; /* usage condition or license, like "MIT", "LGPL" or "BSD-3clause" */ |
496 |
|
|
char *author; /* nickname, email, homepage or github URL etc. */ |
497 |
|
|
char *desc; /* comments, descriptions. May contain '\n' newline character */ |
498 |
|
|
M3D_FLOAT scale; /* the model's bounding cube's size in SI meters */ |
499 |
|
|
M3D_INDEX numcmap; |
500 |
|
|
uint32_t *cmap; /* color map */ |
501 |
|
|
M3D_INDEX numtmap; |
502 |
|
|
m3dti_t *tmap; /* texture map indices */ |
503 |
|
|
M3D_INDEX numtexture; |
504 |
|
|
m3dtx_t *texture; /* uncompressed textures */ |
505 |
|
|
M3D_INDEX numbone; |
506 |
|
|
m3db_t *bone; /* bone hierarchy */ |
507 |
|
|
M3D_INDEX numvertex; |
508 |
|
|
m3dv_t *vertex; /* vertex data */ |
509 |
|
|
M3D_INDEX numskin; |
510 |
|
|
m3ds_t *skin; /* skin data */ |
511 |
|
|
M3D_INDEX nummaterial; |
512 |
|
|
m3dm_t *material; /* material list */ |
513 |
|
|
#ifdef M3D_VERTEXMAX |
514 |
|
|
M3D_INDEX numparam; |
515 |
|
|
m3dvi_t *param; /* parameters and their values list */ |
516 |
|
|
#endif |
517 |
|
|
M3D_INDEX numface; |
518 |
|
|
m3df_t *face; /* model face, polygon (triangle) mesh */ |
519 |
|
|
M3D_INDEX numvoxtype; |
520 |
|
|
m3dvt_t *voxtype; /* model face, voxel types */ |
521 |
|
|
M3D_INDEX numvoxel; |
522 |
|
|
m3dvx_t *voxel; /* model face, cubes compressed into voxels */ |
523 |
|
|
M3D_INDEX numshape; |
524 |
|
|
m3dh_t *shape; /* model face, shape commands */ |
525 |
|
|
M3D_INDEX numlabel; |
526 |
|
|
m3dl_t *label; /* annotation labels */ |
527 |
|
|
M3D_INDEX numaction; |
528 |
|
|
m3da_t *action; /* action animations */ |
529 |
|
|
M3D_INDEX numinlined; |
530 |
|
|
m3di_t *inlined; /* inlined assets */ |
531 |
|
|
M3D_INDEX numextra; |
532 |
|
|
m3dchunk_t **extra; /* unknown chunks, application / engine specific data probably */ |
533 |
|
|
m3di_t preview; /* preview chunk */ |
534 |
|
|
} m3d_t; |
535 |
|
|
|
536 |
|
|
/*** export parameters ***/ |
537 |
|
|
#define M3D_EXP_INT8 0 |
538 |
|
|
#define M3D_EXP_INT16 1 |
539 |
|
|
#define M3D_EXP_FLOAT 2 |
540 |
|
|
#define M3D_EXP_DOUBLE 3 |
541 |
|
|
|
542 |
|
|
#define M3D_EXP_NOCMAP (1<<0) |
543 |
|
|
#define M3D_EXP_NOMATERIAL (1<<1) |
544 |
|
|
#define M3D_EXP_NOFACE (1<<2) |
545 |
|
|
#define M3D_EXP_NONORMAL (1<<3) |
546 |
|
|
#define M3D_EXP_NOTXTCRD (1<<4) |
547 |
|
|
#define M3D_EXP_FLIPTXTCRD (1<<5) |
548 |
|
|
#define M3D_EXP_NORECALC (1<<6) |
549 |
|
|
#define M3D_EXP_IDOSUCK (1<<7) |
550 |
|
|
#define M3D_EXP_NOBONE (1<<8) |
551 |
|
|
#define M3D_EXP_NOACTION (1<<9) |
552 |
|
|
#define M3D_EXP_INLINE (1<<10) |
553 |
|
|
#define M3D_EXP_EXTRA (1<<11) |
554 |
|
|
#define M3D_EXP_NOZLIB (1<<14) |
555 |
|
|
#define M3D_EXP_ASCII (1<<15) |
556 |
|
|
#define M3D_EXP_NOVRTMAX (1<<16) |
557 |
|
|
|
558 |
|
|
/*** error codes ***/ |
559 |
|
|
#define M3D_SUCCESS 0 |
560 |
|
|
#define M3D_ERR_ALLOC -1 |
561 |
|
|
#define M3D_ERR_BADFILE -2 |
562 |
|
|
#define M3D_ERR_UNIMPL -65 |
563 |
|
|
#define M3D_ERR_UNKPROP -66 |
564 |
|
|
#define M3D_ERR_UNKMESH -67 |
565 |
|
|
#define M3D_ERR_UNKIMG -68 |
566 |
|
|
#define M3D_ERR_UNKFRAME -69 |
567 |
|
|
#define M3D_ERR_UNKCMD -70 |
568 |
|
|
#define M3D_ERR_UNKVOX -71 |
569 |
|
|
#define M3D_ERR_TRUNC -72 |
570 |
|
|
#define M3D_ERR_CMAP -73 |
571 |
|
|
#define M3D_ERR_TMAP -74 |
572 |
|
|
#define M3D_ERR_VRTS -75 |
573 |
|
|
#define M3D_ERR_BONE -76 |
574 |
|
|
#define M3D_ERR_MTRL -77 |
575 |
|
|
#define M3D_ERR_SHPE -78 |
576 |
|
|
#define M3D_ERR_VOXT -79 |
577 |
|
|
|
578 |
|
|
#define M3D_ERR_ISFATAL(x) ((x) < 0 && (x) > -65) |
579 |
|
|
|
580 |
|
|
/* callbacks */ |
581 |
|
|
typedef unsigned char *(*m3dread_t)(char *filename, unsigned int *size); /* read file contents into buffer */ |
582 |
|
|
typedef void (*m3dfree_t)(void *buffer); /* free file contents buffer */ |
583 |
|
|
typedef int (*m3dtxsc_t)(const char *name, const void *script, uint32_t len, m3dtx_t *output); /* interpret texture script */ |
584 |
|
|
typedef int (*m3dprsc_t)(const char *name, const void *script, uint32_t len, m3d_t *model); /* interpret surface script */ |
585 |
|
|
#endif /* ifndef M3D_APIVERSION */ |
586 |
|
|
|
587 |
|
|
/*** C prototypes ***/ |
588 |
|
|
/* import / export */ |
589 |
|
|
m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d_t *mtllib); |
590 |
|
|
unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size); |
591 |
|
|
void m3d_free(m3d_t *model); |
592 |
|
|
/* generate animation pose skeleton */ |
593 |
|
|
m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t *skeleton); |
594 |
|
|
m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec); |
595 |
|
|
|
596 |
|
|
/* private prototypes used by both importer and exporter */ |
597 |
|
|
char *_m3d_safestr(char *in, int morelines); |
598 |
|
|
|
599 |
|
|
/*** C implementation ***/ |
600 |
|
|
#ifdef M3D_IMPLEMENTATION |
601 |
|
|
#if !defined(M3D_NOIMPORTER) || defined(M3D_EXPORTER) |
602 |
|
|
/* material property definitions */ |
603 |
|
|
static m3dpd_t m3d_propertytypes[] = { |
604 |
|
|
M3D_PROPERTYDEF(m3dpf_color, m3dp_Kd, "Kd"), /* diffuse color */ |
605 |
|
|
M3D_PROPERTYDEF(m3dpf_color, m3dp_Ka, "Ka"), /* ambient color */ |
606 |
|
|
M3D_PROPERTYDEF(m3dpf_color, m3dp_Ks, "Ks"), /* specular color */ |
607 |
|
|
M3D_PROPERTYDEF(m3dpf_float, m3dp_Ns, "Ns"), /* specular exponent */ |
608 |
|
|
M3D_PROPERTYDEF(m3dpf_color, m3dp_Ke, "Ke"), /* emissive (emitting light of this color) */ |
609 |
|
|
M3D_PROPERTYDEF(m3dpf_color, m3dp_Tf, "Tf"), /* transmission color */ |
610 |
|
|
M3D_PROPERTYDEF(m3dpf_float, m3dp_Km, "Km"), /* bump strength */ |
611 |
|
|
M3D_PROPERTYDEF(m3dpf_float, m3dp_d, "d"), /* dissolve (transparency) */ |
612 |
|
|
M3D_PROPERTYDEF(m3dpf_uint8, m3dp_il, "il"), /* illumination model (informational, ignored by PBR-shaders) */ |
613 |
|
|
|
614 |
|
|
M3D_PROPERTYDEF(m3dpf_float, m3dp_Pr, "Pr"), /* roughness */ |
615 |
|
|
M3D_PROPERTYDEF(m3dpf_float, m3dp_Pm, "Pm"), /* metallic, also reflection */ |
616 |
|
|
M3D_PROPERTYDEF(m3dpf_float, m3dp_Ps, "Ps"), /* sheen */ |
617 |
|
|
M3D_PROPERTYDEF(m3dpf_float, m3dp_Ni, "Ni"), /* index of refraction (optical density) */ |
618 |
|
|
M3D_PROPERTYDEF(m3dpf_float, m3dp_Nt, "Nt"), /* thickness of face in millimeter, for printing */ |
619 |
|
|
|
620 |
|
|
/* aliases, note that "map_*" aliases are handled automatically */ |
621 |
|
|
M3D_PROPERTYDEF(m3dpf_map, m3dp_map_Km, "bump"), |
622 |
|
|
M3D_PROPERTYDEF(m3dpf_map, m3dp_map_N, "map_N"),/* as normal map has no scalar version, it's counterpart is 'il' */ |
623 |
|
|
M3D_PROPERTYDEF(m3dpf_map, m3dp_map_Pm, "refl") |
624 |
|
|
}; |
625 |
|
|
/* shape command definitions. if more commands start with the same string, the longer must come first */ |
626 |
|
|
static m3dcd_t m3d_commandtypes[] = { |
627 |
|
|
/* technical */ |
628 |
|
|
M3D_CMDDEF(m3dc_use, "use", 1, m3dcp_mi_t, 0, 0, 0, 0, 0, 0, 0), |
629 |
|
|
M3D_CMDDEF(m3dc_inc, "inc", 3, m3dcp_hi_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vi_t, 0, 0, 0, 0), |
630 |
|
|
M3D_CMDDEF(m3dc_mesh, "mesh", 1, m3dcp_fi_t, m3dcp_fi_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vi_t, 0, 0, 0), |
631 |
|
|
/* approximations */ |
632 |
|
|
M3D_CMDDEF(m3dc_div, "div", 1, m3dcp_vc_t, 0, 0, 0, 0, 0, 0, 0), |
633 |
|
|
M3D_CMDDEF(m3dc_sub, "sub", 2, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0), |
634 |
|
|
M3D_CMDDEF(m3dc_len, "len", 1, m3dcp_vc_t, 0, 0, 0, 0, 0, 0, 0), |
635 |
|
|
M3D_CMDDEF(m3dc_dist, "dist", 2, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0), |
636 |
|
|
/* modifiers */ |
637 |
|
|
M3D_CMDDEF(m3dc_degu, "degu", 1, m3dcp_i1_t, 0, 0, 0, 0, 0, 0, 0), |
638 |
|
|
M3D_CMDDEF(m3dc_deg, "deg", 2, m3dcp_i1_t, m3dcp_i1_t, 0, 0, 0, 0, 0, 0), |
639 |
|
|
M3D_CMDDEF(m3dc_rangeu, "rangeu", 1, m3dcp_ti_t, 0, 0, 0, 0, 0, 0, 0), |
640 |
|
|
M3D_CMDDEF(m3dc_range, "range", 2, m3dcp_ti_t, m3dcp_ti_t, 0, 0, 0, 0, 0, 0), |
641 |
|
|
M3D_CMDDEF(m3dc_paru, "paru", 2, m3dcp_va_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0), |
642 |
|
|
M3D_CMDDEF(m3dc_parv, "parv", 2, m3dcp_va_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0), |
643 |
|
|
M3D_CMDDEF(m3dc_trim, "trim", 3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0), |
644 |
|
|
M3D_CMDDEF(m3dc_hole, "hole", 3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0), |
645 |
|
|
M3D_CMDDEF(m3dc_scrv, "scrv", 3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0), |
646 |
|
|
M3D_CMDDEF(m3dc_sp, "sp", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), |
647 |
|
|
/* helper curves */ |
648 |
|
|
M3D_CMDDEF(m3dc_bez1, "bez1", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), |
649 |
|
|
M3D_CMDDEF(m3dc_bsp1, "bsp1", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), |
650 |
|
|
M3D_CMDDEF(m3dc_bez2, "bez2", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), |
651 |
|
|
M3D_CMDDEF(m3dc_bsp2, "bsp2", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), |
652 |
|
|
/* surfaces */ |
653 |
|
|
M3D_CMDDEF(m3dc_bezun, "bezun", 4, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, m3dcp_vi_t, 0, 0, 0, 0), |
654 |
|
|
M3D_CMDDEF(m3dc_bezu, "bezu", 3, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, 0, 0, 0, 0, 0), |
655 |
|
|
M3D_CMDDEF(m3dc_bezn, "bezn", 3, m3dcp_va_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0), |
656 |
|
|
M3D_CMDDEF(m3dc_bez, "bez", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), |
657 |
|
|
M3D_CMDDEF(m3dc_nurbsun, "nurbsun", 4, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, m3dcp_vi_t, 0, 0, 0, 0), |
658 |
|
|
M3D_CMDDEF(m3dc_nurbsu, "nurbsu", 3, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, 0, 0, 0, 0, 0), |
659 |
|
|
M3D_CMDDEF(m3dc_nurbsn, "nurbsn", 3, m3dcp_va_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0), |
660 |
|
|
M3D_CMDDEF(m3dc_nurbs, "nurbs", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), |
661 |
|
|
M3D_CMDDEF(m3dc_conn, "conn", 6, m3dcp_i2_t, m3dcp_ti_t, m3dcp_i2_t, m3dcp_i2_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0), |
662 |
|
|
/* geometrical */ |
663 |
|
|
M3D_CMDDEF(m3dc_line, "line", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), |
664 |
|
|
M3D_CMDDEF(m3dc_polygon, "polygon", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0), |
665 |
|
|
M3D_CMDDEF(m3dc_circle, "circle", 3, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, 0, 0, 0, 0, 0), |
666 |
|
|
M3D_CMDDEF(m3dc_cylinder,"cylinder",6, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, 0, 0), |
667 |
|
|
M3D_CMDDEF(m3dc_shpere, "shpere", 2, m3dcp_vi_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0), |
668 |
|
|
M3D_CMDDEF(m3dc_torus, "torus", 4, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0), |
669 |
|
|
M3D_CMDDEF(m3dc_cone, "cone", 3, m3dcp_vi_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0), |
670 |
|
|
M3D_CMDDEF(m3dc_cube, "cube", 3, m3dcp_vi_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0) |
671 |
|
|
}; |
672 |
|
|
#endif |
673 |
|
|
|
674 |
|
|
#include <stdlib.h> |
675 |
|
|
#include <string.h> |
676 |
|
|
|
677 |
|
|
#if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H) |
678 |
|
|
/* PNG decompressor from |
679 |
|
|
|
680 |
|
|
stb_image - v2.23 - public domain image loader - http://nothings.org/stb_image.h |
681 |
|
|
*/ |
682 |
|
|
static const char *_m3dstbi__g_failure_reason; |
683 |
|
|
|
684 |
|
|
enum |
685 |
|
|
{ |
686 |
|
|
STBI_default = 0, |
687 |
|
|
|
688 |
|
|
STBI_grey = 1, |
689 |
|
|
STBI_grey_alpha = 2, |
690 |
|
|
STBI_rgb = 3, |
691 |
|
|
STBI_rgb_alpha = 4 |
692 |
|
|
}; |
693 |
|
|
|
694 |
|
|
enum |
695 |
|
|
{ |
696 |
|
|
STBI__SCAN_load=0, |
697 |
|
|
STBI__SCAN_type, |
698 |
|
|
STBI__SCAN_header |
699 |
|
|
}; |
700 |
|
|
|
701 |
|
|
typedef unsigned short _m3dstbi_us; |
702 |
|
|
|
703 |
|
|
typedef uint16_t _m3dstbi__uint16; |
704 |
|
|
typedef int16_t _m3dstbi__int16; |
705 |
|
|
typedef uint32_t _m3dstbi__uint32; |
706 |
|
|
typedef int32_t _m3dstbi__int32; |
707 |
|
|
|
708 |
|
|
typedef struct |
709 |
|
|
{ |
710 |
|
|
_m3dstbi__uint32 img_x, img_y; |
711 |
|
|
int img_n, img_out_n; |
712 |
|
|
|
713 |
|
|
void *io_user_data; |
714 |
|
|
|
715 |
|
|
int read_from_callbacks; |
716 |
|
|
int buflen; |
717 |
|
|
unsigned char buffer_start[128]; |
718 |
|
|
|
719 |
|
|
unsigned char *img_buffer, *img_buffer_end; |
720 |
|
|
unsigned char *img_buffer_original, *img_buffer_original_end; |
721 |
|
|
} _m3dstbi__context; |
722 |
|
|
|
723 |
|
|
typedef struct |
724 |
|
|
{ |
725 |
|
|
int bits_per_channel; |
726 |
|
|
int num_channels; |
727 |
|
|
int channel_order; |
728 |
|
|
} _m3dstbi__result_info; |
729 |
|
|
|
730 |
|
|
#define STBI_ASSERT(v) |
731 |
|
|
#define STBI_NOTUSED(v) (void)sizeof(v) |
732 |
|
|
#define STBI__BYTECAST(x) ((unsigned char) ((x) & 255)) |
733 |
|
|
#define STBI_MALLOC(sz) M3D_MALLOC(sz) |
734 |
|
|
#define STBI_REALLOC(p,newsz) M3D_REALLOC(p,newsz) |
735 |
|
|
#define STBI_FREE(p) M3D_FREE(p) |
736 |
|
|
#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) |
737 |
|
|
|
738 |
|
|
_inline static unsigned char _m3dstbi__get8(_m3dstbi__context *s) |
739 |
|
|
{ |
740 |
|
✗ |
if (s->img_buffer < s->img_buffer_end) |
741 |
|
✗ |
return *s->img_buffer++; |
742 |
|
|
return 0; |
743 |
|
|
} |
744 |
|
|
|
745 |
|
|
_inline static int _m3dstbi__at_eof(_m3dstbi__context *s) |
746 |
|
|
{ |
747 |
|
|
return s->img_buffer >= s->img_buffer_end; |
748 |
|
|
} |
749 |
|
|
|
750 |
|
|
static void _m3dstbi__skip(_m3dstbi__context *s, int n) |
751 |
|
|
{ |
752 |
|
✗ |
if (n < 0) { |
753 |
|
✗ |
s->img_buffer = s->img_buffer_end; |
754 |
|
✗ |
return; |
755 |
|
|
} |
756 |
|
✗ |
s->img_buffer += n; |
757 |
|
|
} |
758 |
|
|
|
759 |
|
|
static int _m3dstbi__getn(_m3dstbi__context *s, unsigned char *buffer, int n) |
760 |
|
|
{ |
761 |
|
✗ |
if (s->img_buffer+n <= s->img_buffer_end) { |
762 |
|
|
memcpy(buffer, s->img_buffer, n); |
763 |
|
✗ |
s->img_buffer += n; |
764 |
|
|
return 1; |
765 |
|
|
} else |
766 |
|
|
return 0; |
767 |
|
|
} |
768 |
|
|
|
769 |
|
|
static int _m3dstbi__get16be(_m3dstbi__context *s) |
770 |
|
|
{ |
771 |
|
✗ |
int z = _m3dstbi__get8(s); |
772 |
|
✗ |
return (z << 8) + _m3dstbi__get8(s); |
773 |
|
|
} |
774 |
|
|
|
775 |
|
✗ |
static _m3dstbi__uint32 _m3dstbi__get32be(_m3dstbi__context *s) |
776 |
|
|
{ |
777 |
|
✗ |
_m3dstbi__uint32 z = _m3dstbi__get16be(s); |
778 |
|
✗ |
return (z << 16) + _m3dstbi__get16be(s); |
779 |
|
|
} |
780 |
|
|
|
781 |
|
|
#define _m3dstbi__err(x,y) _m3dstbi__errstr(y) |
782 |
|
|
static int _m3dstbi__errstr(const char *str) |
783 |
|
|
{ |
784 |
|
✗ |
_m3dstbi__g_failure_reason = str; |
785 |
|
|
return 0; |
786 |
|
|
} |
787 |
|
|
|
788 |
|
|
_inline static void *_m3dstbi__malloc(size_t size) |
789 |
|
|
{ |
790 |
|
✗ |
return STBI_MALLOC(size); |
791 |
|
|
} |
792 |
|
|
|
793 |
|
|
static int _m3dstbi__addsizes_valid(int a, int b) |
794 |
|
|
{ |
795 |
|
✗ |
if (b < 0) return 0; |
796 |
|
✗ |
return a <= 2147483647 - b; |
797 |
|
|
} |
798 |
|
|
|
799 |
|
|
static int _m3dstbi__mul2sizes_valid(int a, int b) |
800 |
|
|
{ |
801 |
|
✗ |
if (a < 0 || b < 0) return 0; |
802 |
|
✗ |
if (b == 0) return 1; |
803 |
|
✗ |
return a <= 2147483647/b; |
804 |
|
|
} |
805 |
|
|
|
806 |
|
|
static int _m3dstbi__mad2sizes_valid(int a, int b, int add) |
807 |
|
|
{ |
808 |
|
✗ |
return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__addsizes_valid(a*b, add); |
809 |
|
|
} |
810 |
|
|
|
811 |
|
✗ |
static int _m3dstbi__mad3sizes_valid(int a, int b, int c, int add) |
812 |
|
|
{ |
813 |
|
✗ |
return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__mul2sizes_valid(a*b, c) && |
814 |
|
✗ |
_m3dstbi__addsizes_valid(a*b*c, add); |
815 |
|
|
} |
816 |
|
|
|
817 |
|
✗ |
static void *_m3dstbi__malloc_mad2(int a, int b, int add) |
818 |
|
|
{ |
819 |
|
|
if (!_m3dstbi__mad2sizes_valid(a, b, add)) return NULL; |
820 |
|
✗ |
return _m3dstbi__malloc(a*b + add); |
821 |
|
|
} |
822 |
|
|
|
823 |
|
✗ |
static void *_m3dstbi__malloc_mad3(int a, int b, int c, int add) |
824 |
|
|
{ |
825 |
|
✗ |
if (!_m3dstbi__mad3sizes_valid(a, b, c, add)) return NULL; |
826 |
|
✗ |
return _m3dstbi__malloc(a*b*c + add); |
827 |
|
|
} |
828 |
|
|
|
829 |
|
|
static unsigned char _m3dstbi__compute_y(int r, int g, int b) |
830 |
|
|
{ |
831 |
|
✗ |
return (unsigned char) (((r*77) + (g*150) + (29*b)) >> 8); |
832 |
|
|
} |
833 |
|
|
|
834 |
|
✗ |
static unsigned char *_m3dstbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) |
835 |
|
|
{ |
836 |
|
|
int i,j; |
837 |
|
|
unsigned char *good; |
838 |
|
|
|
839 |
|
✗ |
if (req_comp == img_n) return data; |
840 |
|
|
STBI_ASSERT(req_comp >= 1 && req_comp <= 4); |
841 |
|
|
|
842 |
|
✗ |
good = (unsigned char *) _m3dstbi__malloc_mad3(req_comp, x, y, 0); |
843 |
|
✗ |
if (good == NULL) { |
844 |
|
✗ |
STBI_FREE(data); |
845 |
|
|
_m3dstbi__err("outofmem", "Out of memory"); |
846 |
|
✗ |
return NULL; |
847 |
|
|
} |
848 |
|
|
|
849 |
|
✗ |
for (j=0; j < (int) y; ++j) { |
850 |
|
✗ |
unsigned char *src = data + j * x * img_n ; |
851 |
|
✗ |
unsigned char *dest = good + j * x * req_comp; |
852 |
|
|
|
853 |
|
|
#define STBI__COMBO(a,b) ((a)*8+(b)) |
854 |
|
|
#define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) |
855 |
|
✗ |
switch (STBI__COMBO(img_n, req_comp)) { |
856 |
|
✗ |
STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; |
857 |
|
✗ |
STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; |
858 |
|
✗ |
STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; |
859 |
|
✗ |
STBI__CASE(2,1) { dest[0]=src[0]; } break; |
860 |
|
✗ |
STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; |
861 |
|
✗ |
STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; |
862 |
|
✗ |
STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; |
863 |
|
✗ |
STBI__CASE(3,1) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]); } break; |
864 |
|
✗ |
STBI__CASE(3,2) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; |
865 |
|
✗ |
STBI__CASE(4,1) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]); } break; |
866 |
|
✗ |
STBI__CASE(4,2) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; |
867 |
|
✗ |
STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; |
868 |
|
✗ |
default: STBI_ASSERT(0); |
869 |
|
|
} |
870 |
|
|
#undef STBI__CASE |
871 |
|
|
} |
872 |
|
|
|
873 |
|
✗ |
STBI_FREE(data); |
874 |
|
✗ |
return good; |
875 |
|
|
} |
876 |
|
|
|
877 |
|
|
static _m3dstbi__uint16 _m3dstbi__compute_y_16(int r, int g, int b) |
878 |
|
|
{ |
879 |
|
✗ |
return (_m3dstbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); |
880 |
|
|
} |
881 |
|
|
|
882 |
|
✗ |
static _m3dstbi__uint16 *_m3dstbi__convert_format16(_m3dstbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) |
883 |
|
|
{ |
884 |
|
|
int i,j; |
885 |
|
|
_m3dstbi__uint16 *good; |
886 |
|
|
|
887 |
|
✗ |
if (req_comp == img_n) return data; |
888 |
|
|
STBI_ASSERT(req_comp >= 1 && req_comp <= 4); |
889 |
|
|
|
890 |
|
✗ |
good = (_m3dstbi__uint16 *) _m3dstbi__malloc(req_comp * x * y * 2); |
891 |
|
✗ |
if (good == NULL) { |
892 |
|
✗ |
STBI_FREE(data); |
893 |
|
|
_m3dstbi__err("outofmem", "Out of memory"); |
894 |
|
✗ |
return NULL; |
895 |
|
|
} |
896 |
|
|
|
897 |
|
✗ |
for (j=0; j < (int) y; ++j) { |
898 |
|
✗ |
_m3dstbi__uint16 *src = data + j * x * img_n ; |
899 |
|
✗ |
_m3dstbi__uint16 *dest = good + j * x * req_comp; |
900 |
|
|
|
901 |
|
|
#define STBI__COMBO(a,b) ((a)*8+(b)) |
902 |
|
|
#define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) |
903 |
|
✗ |
switch (STBI__COMBO(img_n, req_comp)) { |
904 |
|
✗ |
STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; |
905 |
|
✗ |
STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; |
906 |
|
✗ |
STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; |
907 |
|
✗ |
STBI__CASE(2,1) { dest[0]=src[0]; } break; |
908 |
|
✗ |
STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; |
909 |
|
✗ |
STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; |
910 |
|
✗ |
STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; |
911 |
|
✗ |
STBI__CASE(3,1) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]); } break; |
912 |
|
✗ |
STBI__CASE(3,2) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; |
913 |
|
✗ |
STBI__CASE(4,1) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]); } break; |
914 |
|
✗ |
STBI__CASE(4,2) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; |
915 |
|
✗ |
STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; |
916 |
|
✗ |
default: STBI_ASSERT(0); |
917 |
|
|
} |
918 |
|
|
#undef STBI__CASE |
919 |
|
|
} |
920 |
|
|
|
921 |
|
✗ |
STBI_FREE(data); |
922 |
|
✗ |
return good; |
923 |
|
|
} |
924 |
|
|
|
925 |
|
|
#define STBI__ZFAST_BITS 9 |
926 |
|
|
#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) |
927 |
|
|
|
928 |
|
|
typedef struct |
929 |
|
|
{ |
930 |
|
|
_m3dstbi__uint16 fast[1 << STBI__ZFAST_BITS]; |
931 |
|
|
_m3dstbi__uint16 firstcode[16]; |
932 |
|
|
int maxcode[17]; |
933 |
|
|
_m3dstbi__uint16 firstsymbol[16]; |
934 |
|
|
unsigned char size[288]; |
935 |
|
|
_m3dstbi__uint16 value[288]; |
936 |
|
|
} _m3dstbi__zhuffman; |
937 |
|
|
|
938 |
|
✗ |
_inline static int _m3dstbi__bitreverse16(int n) |
939 |
|
|
{ |
940 |
|
✗ |
n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); |
941 |
|
✗ |
n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); |
942 |
|
✗ |
n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); |
943 |
|
✗ |
n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); |
944 |
|
✗ |
return n; |
945 |
|
|
} |
946 |
|
|
|
947 |
|
|
_inline static int _m3dstbi__bit_reverse(int v, int bits) |
948 |
|
|
{ |
949 |
|
|
STBI_ASSERT(bits <= 16); |
950 |
|
✗ |
return _m3dstbi__bitreverse16(v) >> (16-bits); |
951 |
|
|
} |
952 |
|
|
|
953 |
|
✗ |
static int _m3dstbi__zbuild_huffman(_m3dstbi__zhuffman *z, unsigned char *sizelist, int num) |
954 |
|
|
{ |
955 |
|
|
int i,k=0; |
956 |
|
|
int code, next_code[16], sizes[17]; |
957 |
|
|
|
958 |
|
|
memset(sizes, 0, sizeof(sizes)); |
959 |
|
✗ |
memset(z->fast, 0, sizeof(z->fast)); |
960 |
|
✗ |
for (i=0; i < num; ++i) |
961 |
|
✗ |
++sizes[sizelist[i]]; |
962 |
|
✗ |
sizes[0] = 0; |
963 |
|
✗ |
for (i=1; i < 16; ++i) |
964 |
|
✗ |
if (sizes[i] > (1 << i)) |
965 |
|
✗ |
return _m3dstbi__err("bad sizes", "Corrupt PNG"); |
966 |
|
|
code = 0; |
967 |
|
✗ |
for (i=1; i < 16; ++i) { |
968 |
|
✗ |
next_code[i] = code; |
969 |
|
✗ |
z->firstcode[i] = (_m3dstbi__uint16) code; |
970 |
|
✗ |
z->firstsymbol[i] = (_m3dstbi__uint16) k; |
971 |
|
✗ |
code = (code + sizes[i]); |
972 |
|
✗ |
if (sizes[i]) |
973 |
|
✗ |
if (code-1 >= (1 << i)) return _m3dstbi__err("bad codelengths","Corrupt PNG"); |
974 |
|
✗ |
z->maxcode[i] = code << (16-i); |
975 |
|
✗ |
code <<= 1; |
976 |
|
✗ |
k += sizes[i]; |
977 |
|
|
} |
978 |
|
✗ |
z->maxcode[16] = 0x10000; |
979 |
|
✗ |
for (i=0; i < num; ++i) { |
980 |
|
✗ |
int s = sizelist[i]; |
981 |
|
✗ |
if (s) { |
982 |
|
✗ |
int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; |
983 |
|
✗ |
_m3dstbi__uint16 fastv = (_m3dstbi__uint16) ((s << 9) | i); |
984 |
|
✗ |
z->size [c] = (unsigned char ) s; |
985 |
|
✗ |
z->value[c] = (_m3dstbi__uint16) i; |
986 |
|
✗ |
if (s <= STBI__ZFAST_BITS) { |
987 |
|
|
int j = _m3dstbi__bit_reverse(next_code[s],s); |
988 |
|
✗ |
while (j < (1 << STBI__ZFAST_BITS)) { |
989 |
|
✗ |
z->fast[j] = fastv; |
990 |
|
✗ |
j += (1 << s); |
991 |
|
|
} |
992 |
|
|
} |
993 |
|
✗ |
++next_code[s]; |
994 |
|
|
} |
995 |
|
|
} |
996 |
|
|
return 1; |
997 |
|
|
} |
998 |
|
|
|
999 |
|
|
typedef struct |
1000 |
|
|
{ |
1001 |
|
|
unsigned char *zbuffer, *zbuffer_end; |
1002 |
|
|
int num_bits; |
1003 |
|
|
_m3dstbi__uint32 code_buffer; |
1004 |
|
|
|
1005 |
|
|
char *zout; |
1006 |
|
|
char *zout_start; |
1007 |
|
|
char *zout_end; |
1008 |
|
|
int z_expandable; |
1009 |
|
|
|
1010 |
|
|
_m3dstbi__zhuffman z_length, z_distance; |
1011 |
|
|
} _m3dstbi__zbuf; |
1012 |
|
|
|
1013 |
|
|
_inline static unsigned char _m3dstbi__zget8(_m3dstbi__zbuf *z) |
1014 |
|
|
{ |
1015 |
|
✗ |
if (z->zbuffer >= z->zbuffer_end) return 0; |
1016 |
|
✗ |
return *z->zbuffer++; |
1017 |
|
|
} |
1018 |
|
|
|
1019 |
|
|
static void _m3dstbi__fill_bits(_m3dstbi__zbuf *z) |
1020 |
|
|
{ |
1021 |
|
|
do { |
1022 |
|
|
STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); |
1023 |
|
✗ |
z->code_buffer |= (unsigned int) _m3dstbi__zget8(z) << z->num_bits; |
1024 |
|
✗ |
z->num_bits += 8; |
1025 |
|
✗ |
} while (z->num_bits <= 24); |
1026 |
|
|
} |
1027 |
|
|
|
1028 |
|
✗ |
_inline static unsigned int _m3dstbi__zreceive(_m3dstbi__zbuf *z, int n) |
1029 |
|
|
{ |
1030 |
|
|
unsigned int k; |
1031 |
|
✗ |
if (z->num_bits < n) _m3dstbi__fill_bits(z); |
1032 |
|
✗ |
k = z->code_buffer & ((1 << n) - 1); |
1033 |
|
✗ |
z->code_buffer >>= n; |
1034 |
|
✗ |
z->num_bits -= n; |
1035 |
|
✗ |
return k; |
1036 |
|
|
} |
1037 |
|
|
|
1038 |
|
✗ |
static int _m3dstbi__zhuffman_decode_slowpath(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) |
1039 |
|
|
{ |
1040 |
|
|
int b,s,k; |
1041 |
|
✗ |
k = _m3dstbi__bit_reverse(a->code_buffer, 16); |
1042 |
|
✗ |
for (s=STBI__ZFAST_BITS+1; ; ++s) |
1043 |
|
✗ |
if (k < z->maxcode[s]) |
1044 |
|
|
break; |
1045 |
|
✗ |
if (s == 16) return -1; |
1046 |
|
✗ |
b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; |
1047 |
|
|
STBI_ASSERT(z->size[b] == s); |
1048 |
|
✗ |
a->code_buffer >>= s; |
1049 |
|
✗ |
a->num_bits -= s; |
1050 |
|
✗ |
return z->value[b]; |
1051 |
|
|
} |
1052 |
|
|
|
1053 |
|
✗ |
_inline static int _m3dstbi__zhuffman_decode(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) |
1054 |
|
|
{ |
1055 |
|
|
int b,s; |
1056 |
|
✗ |
if (a->num_bits < 16) _m3dstbi__fill_bits(a); |
1057 |
|
✗ |
b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; |
1058 |
|
✗ |
if (b) { |
1059 |
|
✗ |
s = b >> 9; |
1060 |
|
✗ |
a->code_buffer >>= s; |
1061 |
|
✗ |
a->num_bits -= s; |
1062 |
|
✗ |
return b & 511; |
1063 |
|
|
} |
1064 |
|
✗ |
return _m3dstbi__zhuffman_decode_slowpath(a, z); |
1065 |
|
|
} |
1066 |
|
|
|
1067 |
|
✗ |
static int _m3dstbi__zexpand(_m3dstbi__zbuf *z, char *zout, int n) |
1068 |
|
|
{ |
1069 |
|
|
char *q; |
1070 |
|
|
int cur, limit, old_limit; |
1071 |
|
✗ |
z->zout = zout; |
1072 |
|
✗ |
if (!z->z_expandable) return _m3dstbi__err("output buffer limit","Corrupt PNG"); |
1073 |
|
✗ |
cur = (int) (z->zout - z->zout_start); |
1074 |
|
✗ |
limit = old_limit = (int) (z->zout_end - z->zout_start); |
1075 |
|
✗ |
while (cur + n > limit) |
1076 |
|
✗ |
limit *= 2; |
1077 |
|
✗ |
q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); |
1078 |
|
|
STBI_NOTUSED(old_limit); |
1079 |
|
✗ |
if (q == NULL) return _m3dstbi__err("outofmem", "Out of memory"); |
1080 |
|
✗ |
z->zout_start = q; |
1081 |
|
✗ |
z->zout = q + cur; |
1082 |
|
✗ |
z->zout_end = q + limit; |
1083 |
|
✗ |
return 1; |
1084 |
|
|
} |
1085 |
|
|
|
1086 |
|
|
static int _m3dstbi__zlength_base[31] = { |
1087 |
|
|
3,4,5,6,7,8,9,10,11,13, |
1088 |
|
|
15,17,19,23,27,31,35,43,51,59, |
1089 |
|
|
67,83,99,115,131,163,195,227,258,0,0 }; |
1090 |
|
|
|
1091 |
|
|
static int _m3dstbi__zlength_extra[31]= |
1092 |
|
|
{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; |
1093 |
|
|
|
1094 |
|
|
static int _m3dstbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, |
1095 |
|
|
257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; |
1096 |
|
|
|
1097 |
|
|
static int _m3dstbi__zdist_extra[32] = |
1098 |
|
|
{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; |
1099 |
|
|
|
1100 |
|
✗ |
static int _m3dstbi__parse_huffman_block(_m3dstbi__zbuf *a) |
1101 |
|
|
{ |
1102 |
|
✗ |
char *zout = a->zout; |
1103 |
|
|
for(;;) { |
1104 |
|
✗ |
int z = _m3dstbi__zhuffman_decode(a, &a->z_length); |
1105 |
|
✗ |
if (z < 256) { |
1106 |
|
✗ |
if (z < 0) return _m3dstbi__err("bad huffman code","Corrupt PNG"); |
1107 |
|
✗ |
if (zout >= a->zout_end) { |
1108 |
|
✗ |
if (!_m3dstbi__zexpand(a, zout, 1)) return 0; |
1109 |
|
✗ |
zout = a->zout; |
1110 |
|
|
} |
1111 |
|
✗ |
*zout++ = (char) z; |
1112 |
|
|
} else { |
1113 |
|
|
unsigned char *p; |
1114 |
|
|
int len,dist; |
1115 |
|
✗ |
if (z == 256) { |
1116 |
|
✗ |
a->zout = zout; |
1117 |
|
✗ |
return 1; |
1118 |
|
|
} |
1119 |
|
✗ |
z -= 257; |
1120 |
|
✗ |
len = _m3dstbi__zlength_base[z]; |
1121 |
|
✗ |
if (_m3dstbi__zlength_extra[z]) len += _m3dstbi__zreceive(a, _m3dstbi__zlength_extra[z]); |
1122 |
|
✗ |
z = _m3dstbi__zhuffman_decode(a, &a->z_distance); |
1123 |
|
✗ |
if (z < 0) return _m3dstbi__err("bad huffman code","Corrupt PNG"); |
1124 |
|
✗ |
dist = _m3dstbi__zdist_base[z]; |
1125 |
|
✗ |
if (_m3dstbi__zdist_extra[z]) dist += _m3dstbi__zreceive(a, _m3dstbi__zdist_extra[z]); |
1126 |
|
✗ |
if (zout - a->zout_start < dist) return _m3dstbi__err("bad dist","Corrupt PNG"); |
1127 |
|
✗ |
if (zout + len > a->zout_end) { |
1128 |
|
✗ |
if (!_m3dstbi__zexpand(a, zout, len)) return 0; |
1129 |
|
✗ |
zout = a->zout; |
1130 |
|
|
} |
1131 |
|
✗ |
p = (unsigned char *) (zout - dist); |
1132 |
|
✗ |
if (dist == 1) { |
1133 |
|
✗ |
unsigned char v = *p; |
1134 |
|
✗ |
if (len) { do *zout++ = v; while (--len); } |
1135 |
|
|
} else { |
1136 |
|
✗ |
if (len) { do *zout++ = *p++; while (--len); } |
1137 |
|
|
} |
1138 |
|
|
} |
1139 |
|
|
} |
1140 |
|
|
} |
1141 |
|
|
|
1142 |
|
✗ |
static int _m3dstbi__compute_huffman_codes(_m3dstbi__zbuf *a) |
1143 |
|
|
{ |
1144 |
|
|
static unsigned char length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; |
1145 |
|
|
_m3dstbi__zhuffman z_codelength; |
1146 |
|
|
unsigned char lencodes[286+32+137]; |
1147 |
|
|
unsigned char codelength_sizes[19]; |
1148 |
|
|
int i,n; |
1149 |
|
|
|
1150 |
|
✗ |
int hlit = _m3dstbi__zreceive(a,5) + 257; |
1151 |
|
✗ |
int hdist = _m3dstbi__zreceive(a,5) + 1; |
1152 |
|
✗ |
int hclen = _m3dstbi__zreceive(a,4) + 4; |
1153 |
|
✗ |
int ntot = hlit + hdist; |
1154 |
|
|
|
1155 |
|
|
memset(codelength_sizes, 0, sizeof(codelength_sizes)); |
1156 |
|
✗ |
for (i=0; i < hclen; ++i) { |
1157 |
|
✗ |
int s = _m3dstbi__zreceive(a,3); |
1158 |
|
✗ |
codelength_sizes[length_dezigzag[i]] = (unsigned char) s; |
1159 |
|
|
} |
1160 |
|
✗ |
if (!_m3dstbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; |
1161 |
|
|
|
1162 |
|
|
n = 0; |
1163 |
|
✗ |
while (n < ntot) { |
1164 |
|
✗ |
int c = _m3dstbi__zhuffman_decode(a, &z_codelength); |
1165 |
|
✗ |
if (c < 0 || c >= 19) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); |
1166 |
|
✗ |
if (c < 16) |
1167 |
|
✗ |
lencodes[n++] = (unsigned char) c; |
1168 |
|
|
else { |
1169 |
|
|
unsigned char fill = 0; |
1170 |
|
✗ |
if (c == 16) { |
1171 |
|
✗ |
c = _m3dstbi__zreceive(a,2)+3; |
1172 |
|
✗ |
if (n == 0) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); |
1173 |
|
✗ |
fill = lencodes[n-1]; |
1174 |
|
✗ |
} else if (c == 17) |
1175 |
|
✗ |
c = _m3dstbi__zreceive(a,3)+3; |
1176 |
|
|
else { |
1177 |
|
|
STBI_ASSERT(c == 18); |
1178 |
|
✗ |
c = _m3dstbi__zreceive(a,7)+11; |
1179 |
|
|
} |
1180 |
|
✗ |
if (ntot - n < c) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); |
1181 |
|
✗ |
memset(lencodes+n, fill, c); |
1182 |
|
✗ |
n += c; |
1183 |
|
|
} |
1184 |
|
|
} |
1185 |
|
✗ |
if (n != ntot) return _m3dstbi__err("bad codelengths","Corrupt PNG"); |
1186 |
|
✗ |
if (!_m3dstbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; |
1187 |
|
✗ |
if (!_m3dstbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; |
1188 |
|
|
return 1; |
1189 |
|
|
} |
1190 |
|
|
|
1191 |
|
✗ |
_inline static int _m3dstbi__parse_uncompressed_block(_m3dstbi__zbuf *a) |
1192 |
|
|
{ |
1193 |
|
|
unsigned char header[4]; |
1194 |
|
|
int len,nlen,k; |
1195 |
|
✗ |
if (a->num_bits & 7) |
1196 |
|
✗ |
_m3dstbi__zreceive(a, a->num_bits & 7); |
1197 |
|
|
k = 0; |
1198 |
|
✗ |
while (a->num_bits > 0) { |
1199 |
|
✗ |
header[k++] = (unsigned char) (a->code_buffer & 255); |
1200 |
|
✗ |
a->code_buffer >>= 8; |
1201 |
|
✗ |
a->num_bits -= 8; |
1202 |
|
|
} |
1203 |
|
|
STBI_ASSERT(a->num_bits == 0); |
1204 |
|
✗ |
while (k < 4) |
1205 |
|
✗ |
header[k++] = _m3dstbi__zget8(a); |
1206 |
|
✗ |
len = header[1] * 256 + header[0]; |
1207 |
|
✗ |
nlen = header[3] * 256 + header[2]; |
1208 |
|
✗ |
if (nlen != (len ^ 0xffff)) return _m3dstbi__err("zlib corrupt","Corrupt PNG"); |
1209 |
|
✗ |
if (a->zbuffer + len > a->zbuffer_end) return _m3dstbi__err("read past buffer","Corrupt PNG"); |
1210 |
|
✗ |
if (a->zout + len > a->zout_end) |
1211 |
|
✗ |
if (!_m3dstbi__zexpand(a, a->zout, len)) return 0; |
1212 |
|
✗ |
memcpy(a->zout, a->zbuffer, len); |
1213 |
|
✗ |
a->zbuffer += len; |
1214 |
|
✗ |
a->zout += len; |
1215 |
|
✗ |
return 1; |
1216 |
|
|
} |
1217 |
|
|
|
1218 |
|
✗ |
static int _m3dstbi__parse_zlib_header(_m3dstbi__zbuf *a) |
1219 |
|
|
{ |
1220 |
|
✗ |
int cmf = _m3dstbi__zget8(a); |
1221 |
|
|
int cm = cmf & 15; |
1222 |
|
|
/* int cinfo = cmf >> 4; */ |
1223 |
|
✗ |
int flg = _m3dstbi__zget8(a); |
1224 |
|
✗ |
if ((cmf*256+flg) % 31 != 0) return _m3dstbi__err("bad zlib header","Corrupt PNG"); |
1225 |
|
✗ |
if (flg & 32) return _m3dstbi__err("no preset dict","Corrupt PNG"); |
1226 |
|
✗ |
if (cm != 8) return _m3dstbi__err("bad compression","Corrupt PNG"); |
1227 |
|
|
return 1; |
1228 |
|
|
} |
1229 |
|
|
|
1230 |
|
|
static unsigned char _m3dstbi__zdefault_length[288], _m3dstbi__zdefault_distance[32]; |
1231 |
|
✗ |
static void _m3dstbi__init_zdefaults(void) |
1232 |
|
|
{ |
1233 |
|
|
int i; |
1234 |
|
✗ |
for (i=0; i <= 143; ++i) _m3dstbi__zdefault_length[i] = 8; |
1235 |
|
✗ |
for ( ; i <= 255; ++i) _m3dstbi__zdefault_length[i] = 9; |
1236 |
|
✗ |
for ( ; i <= 279; ++i) _m3dstbi__zdefault_length[i] = 7; |
1237 |
|
✗ |
for ( ; i <= 287; ++i) _m3dstbi__zdefault_length[i] = 8; |
1238 |
|
|
|
1239 |
|
✗ |
for (i=0; i <= 31; ++i) _m3dstbi__zdefault_distance[i] = 5; |
1240 |
|
|
} |
1241 |
|
|
|
1242 |
|
✗ |
static int _m3dstbi__parse_zlib(_m3dstbi__zbuf *a, int parse_header) |
1243 |
|
|
{ |
1244 |
|
|
int final, type; |
1245 |
|
✗ |
if (parse_header) |
1246 |
|
✗ |
if (!_m3dstbi__parse_zlib_header(a)) return 0; |
1247 |
|
✗ |
a->num_bits = 0; |
1248 |
|
✗ |
a->code_buffer = 0; |
1249 |
|
|
do { |
1250 |
|
✗ |
final = _m3dstbi__zreceive(a,1); |
1251 |
|
✗ |
type = _m3dstbi__zreceive(a,2); |
1252 |
|
✗ |
if (type == 0) { |
1253 |
|
✗ |
if (!_m3dstbi__parse_uncompressed_block(a)) return 0; |
1254 |
|
✗ |
} else if (type == 3) { |
1255 |
|
|
return 0; |
1256 |
|
|
} else { |
1257 |
|
✗ |
if (type == 1) { |
1258 |
|
✗ |
if (!_m3dstbi__zbuild_huffman(&a->z_length , _m3dstbi__zdefault_length , 288)) return 0; |
1259 |
|
✗ |
if (!_m3dstbi__zbuild_huffman(&a->z_distance, _m3dstbi__zdefault_distance, 32)) return 0; |
1260 |
|
|
} else { |
1261 |
|
✗ |
if (!_m3dstbi__compute_huffman_codes(a)) return 0; |
1262 |
|
|
} |
1263 |
|
✗ |
if (!_m3dstbi__parse_huffman_block(a)) return 0; |
1264 |
|
|
} |
1265 |
|
✗ |
} while (!final); |
1266 |
|
|
return 1; |
1267 |
|
|
} |
1268 |
|
|
|
1269 |
|
|
static int _m3dstbi__do_zlib(_m3dstbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) |
1270 |
|
|
{ |
1271 |
|
✗ |
a->zout_start = obuf; |
1272 |
|
✗ |
a->zout = obuf; |
1273 |
|
✗ |
a->zout_end = obuf + olen; |
1274 |
|
✗ |
a->z_expandable = exp; |
1275 |
|
✗ |
_m3dstbi__init_zdefaults(); |
1276 |
|
✗ |
return _m3dstbi__parse_zlib(a, parse_header); |
1277 |
|
|
} |
1278 |
|
|
|
1279 |
|
✗ |
char *_m3dstbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) |
1280 |
|
|
{ |
1281 |
|
|
_m3dstbi__zbuf a; |
1282 |
|
✗ |
char *p = (char *) _m3dstbi__malloc(initial_size); |
1283 |
|
✗ |
if (p == NULL) return NULL; |
1284 |
|
✗ |
a.zbuffer = (unsigned char *) buffer; |
1285 |
|
✗ |
a.zbuffer_end = (unsigned char *) buffer + len; |
1286 |
|
✗ |
if (_m3dstbi__do_zlib(&a, p, initial_size, 1, parse_header)) { |
1287 |
|
✗ |
if (outlen) *outlen = (int) (a.zout - a.zout_start); |
1288 |
|
✗ |
return a.zout_start; |
1289 |
|
|
} else { |
1290 |
|
✗ |
STBI_FREE(a.zout_start); |
1291 |
|
✗ |
return NULL; |
1292 |
|
|
} |
1293 |
|
|
} |
1294 |
|
|
|
1295 |
|
|
typedef struct |
1296 |
|
|
{ |
1297 |
|
|
_m3dstbi__uint32 length; |
1298 |
|
|
_m3dstbi__uint32 type; |
1299 |
|
|
} _m3dstbi__pngchunk; |
1300 |
|
|
|
1301 |
|
✗ |
static _m3dstbi__pngchunk _m3dstbi__get_chunk_header(_m3dstbi__context *s) |
1302 |
|
|
{ |
1303 |
|
|
_m3dstbi__pngchunk c; |
1304 |
|
✗ |
c.length = _m3dstbi__get32be(s); |
1305 |
|
✗ |
c.type = _m3dstbi__get32be(s); |
1306 |
|
✗ |
return c; |
1307 |
|
|
} |
1308 |
|
|
|
1309 |
|
|
_inline static int _m3dstbi__check_png_header(_m3dstbi__context *s) |
1310 |
|
|
{ |
1311 |
|
|
static unsigned char png_sig[8] = { 137,80,78,71,13,10,26,10 }; |
1312 |
|
|
int i; |
1313 |
|
✗ |
for (i=0; i < 8; ++i) |
1314 |
|
✗ |
if (_m3dstbi__get8(s) != png_sig[i]) return _m3dstbi__err("bad png sig","Not a PNG"); |
1315 |
|
|
return 1; |
1316 |
|
|
} |
1317 |
|
|
|
1318 |
|
|
typedef struct |
1319 |
|
|
{ |
1320 |
|
|
_m3dstbi__context *s; |
1321 |
|
|
unsigned char *idata, *expanded, *out; |
1322 |
|
|
int depth; |
1323 |
|
|
} _m3dstbi__png; |
1324 |
|
|
|
1325 |
|
|
|
1326 |
|
|
enum { |
1327 |
|
|
STBI__F_none=0, |
1328 |
|
|
STBI__F_sub=1, |
1329 |
|
|
STBI__F_up=2, |
1330 |
|
|
STBI__F_avg=3, |
1331 |
|
|
STBI__F_paeth=4, |
1332 |
|
|
STBI__F_avg_first, |
1333 |
|
|
STBI__F_paeth_first |
1334 |
|
|
}; |
1335 |
|
|
|
1336 |
|
|
static unsigned char first_row_filter[5] = |
1337 |
|
|
{ |
1338 |
|
|
STBI__F_none, |
1339 |
|
|
STBI__F_sub, |
1340 |
|
|
STBI__F_none, |
1341 |
|
|
STBI__F_avg_first, |
1342 |
|
|
STBI__F_paeth_first |
1343 |
|
|
}; |
1344 |
|
|
|
1345 |
|
|
static int _m3dstbi__paeth(int a, int b, int c) |
1346 |
|
|
{ |
1347 |
|
✗ |
int p = a + b - c; |
1348 |
|
✗ |
int pa = abs(p-a); |
1349 |
|
✗ |
int pb = abs(p-b); |
1350 |
|
✗ |
int pc = abs(p-c); |
1351 |
|
✗ |
if (pa <= pb && pa <= pc) return a; |
1352 |
|
✗ |
if (pb <= pc) return b; |
1353 |
|
|
return c; |
1354 |
|
|
} |
1355 |
|
|
|
1356 |
|
|
static unsigned char _m3dstbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; |
1357 |
|
|
|
1358 |
|
✗ |
static int _m3dstbi__create_png_image_raw(_m3dstbi__png *a, unsigned char *raw, _m3dstbi__uint32 raw_len, int out_n, _m3dstbi__uint32 x, _m3dstbi__uint32 y, int depth, int color) |
1359 |
|
|
{ |
1360 |
|
✗ |
int bytes = (depth == 16? 2 : 1); |
1361 |
|
✗ |
_m3dstbi__context *s = a->s; |
1362 |
|
✗ |
_m3dstbi__uint32 i,j,stride = x*out_n*bytes; |
1363 |
|
|
_m3dstbi__uint32 img_len, img_width_bytes; |
1364 |
|
|
int k; |
1365 |
|
✗ |
int img_n = s->img_n; |
1366 |
|
|
|
1367 |
|
✗ |
int output_bytes = out_n*bytes; |
1368 |
|
✗ |
int filter_bytes = img_n*bytes; |
1369 |
|
✗ |
int width = x; |
1370 |
|
|
|
1371 |
|
|
STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); |
1372 |
|
✗ |
a->out = (unsigned char *) _m3dstbi__malloc_mad3(x, y, output_bytes, 0); |
1373 |
|
✗ |
if (!a->out) return _m3dstbi__err("outofmem", "Out of memory"); |
1374 |
|
|
|
1375 |
|
✗ |
if (!_m3dstbi__mad3sizes_valid(img_n, x, depth, 7)) return _m3dstbi__err("too large", "Corrupt PNG"); |
1376 |
|
✗ |
img_width_bytes = (((img_n * x * depth) + 7) >> 3); |
1377 |
|
✗ |
img_len = (img_width_bytes + 1) * y; |
1378 |
|
✗ |
if (s->img_x == x && s->img_y == y) { |
1379 |
|
✗ |
if (raw_len != img_len) return _m3dstbi__err("not enough pixels","Corrupt PNG"); |
1380 |
|
|
} else { |
1381 |
|
✗ |
if (raw_len < img_len) return _m3dstbi__err("not enough pixels","Corrupt PNG"); |
1382 |
|
|
} |
1383 |
|
|
|
1384 |
|
✗ |
for (j=0; j < y; ++j) { |
1385 |
|
✗ |
unsigned char *cur = a->out + stride*j; |
1386 |
|
✗ |
unsigned char *prior = cur - stride; |
1387 |
|
✗ |
int filter = *raw++; |
1388 |
|
|
|
1389 |
|
✗ |
if (filter > 4) |
1390 |
|
✗ |
return _m3dstbi__err("invalid filter","Corrupt PNG"); |
1391 |
|
|
|
1392 |
|
✗ |
if (depth < 8) { |
1393 |
|
|
STBI_ASSERT(img_width_bytes <= x); |
1394 |
|
✗ |
cur += x*out_n - img_width_bytes; |
1395 |
|
|
filter_bytes = 1; |
1396 |
|
✗ |
width = img_width_bytes; |
1397 |
|
|
} |
1398 |
|
✗ |
prior = cur - stride; |
1399 |
|
|
|
1400 |
|
✗ |
if (j == 0) filter = first_row_filter[filter]; |
1401 |
|
|
|
1402 |
|
✗ |
for (k=0; k < filter_bytes; ++k) { |
1403 |
|
✗ |
switch (filter) { |
1404 |
|
✗ |
case STBI__F_none : cur[k] = raw[k]; break; |
1405 |
|
✗ |
case STBI__F_sub : cur[k] = raw[k]; break; |
1406 |
|
✗ |
case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; |
1407 |
|
✗ |
case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; |
1408 |
|
✗ |
case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(0,prior[k],0)); break; |
1409 |
|
✗ |
case STBI__F_avg_first : cur[k] = raw[k]; break; |
1410 |
|
✗ |
case STBI__F_paeth_first: cur[k] = raw[k]; break; |
1411 |
|
|
} |
1412 |
|
|
} |
1413 |
|
|
|
1414 |
|
✗ |
if (depth == 8) { |
1415 |
|
✗ |
if (img_n != out_n) |
1416 |
|
✗ |
cur[img_n] = 255; |
1417 |
|
✗ |
raw += img_n; |
1418 |
|
✗ |
cur += out_n; |
1419 |
|
✗ |
prior += out_n; |
1420 |
|
✗ |
} else if (depth == 16) { |
1421 |
|
✗ |
if (img_n != out_n) { |
1422 |
|
✗ |
cur[filter_bytes] = 255; |
1423 |
|
✗ |
cur[filter_bytes+1] = 255; |
1424 |
|
|
} |
1425 |
|
✗ |
raw += filter_bytes; |
1426 |
|
✗ |
cur += output_bytes; |
1427 |
|
✗ |
prior += output_bytes; |
1428 |
|
|
} else { |
1429 |
|
✗ |
raw += 1; |
1430 |
|
✗ |
cur += 1; |
1431 |
|
✗ |
prior += 1; |
1432 |
|
|
} |
1433 |
|
|
|
1434 |
|
✗ |
if (depth < 8 || img_n == out_n) { |
1435 |
|
✗ |
int nk = (width - 1)*filter_bytes; |
1436 |
|
|
#define STBI__CASE(f) \ |
1437 |
|
|
case f: \ |
1438 |
|
|
for (k=0; k < nk; ++k) |
1439 |
|
✗ |
switch (filter) { |
1440 |
|
✗ |
case STBI__F_none: memcpy(cur, raw, nk); break; |
1441 |
|
✗ |
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; |
1442 |
|
✗ |
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; |
1443 |
|
✗ |
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; |
1444 |
|
✗ |
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; |
1445 |
|
✗ |
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; |
1446 |
|
✗ |
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k-filter_bytes],0,0)); } break; |
1447 |
|
|
} |
1448 |
|
|
#undef STBI__CASE |
1449 |
|
✗ |
raw += nk; |
1450 |
|
|
} else { |
1451 |
|
|
STBI_ASSERT(img_n+1 == out_n); |
1452 |
|
|
#define STBI__CASE(f) \ |
1453 |
|
|
case f: \ |
1454 |
|
|
for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ |
1455 |
|
|
for (k=0; k < filter_bytes; ++k) |
1456 |
|
✗ |
switch (filter) { |
1457 |
|
✗ |
STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; |
1458 |
|
✗ |
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; |
1459 |
|
✗ |
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; |
1460 |
|
✗ |
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; |
1461 |
|
✗ |
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; |
1462 |
|
✗ |
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; |
1463 |
|
✗ |
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k- output_bytes],0,0)); } break; |
1464 |
|
|
} |
1465 |
|
|
#undef STBI__CASE |
1466 |
|
|
|
1467 |
|
✗ |
if (depth == 16) { |
1468 |
|
✗ |
cur = a->out + stride*j; |
1469 |
|
✗ |
for (i=0; i < x; ++i,cur+=output_bytes) { |
1470 |
|
✗ |
cur[filter_bytes+1] = 255; |
1471 |
|
|
} |
1472 |
|
|
} |
1473 |
|
|
} |
1474 |
|
|
} |
1475 |
|
|
|
1476 |
|
✗ |
if (depth < 8) { |
1477 |
|
✗ |
for (j=0; j < y; ++j) { |
1478 |
|
✗ |
unsigned char *cur = a->out + stride*j; |
1479 |
|
✗ |
unsigned char *in = a->out + stride*j + x*out_n - img_width_bytes; |
1480 |
|
✗ |
unsigned char scale = (color == 0) ? _m3dstbi__depth_scale_table[depth] : 1; |
1481 |
|
|
|
1482 |
|
✗ |
if (depth == 4) { |
1483 |
|
✗ |
for (k=x*img_n; k >= 2; k-=2, ++in) { |
1484 |
|
✗ |
*cur++ = scale * ((*in >> 4) ); |
1485 |
|
✗ |
*cur++ = scale * ((*in ) & 0x0f); |
1486 |
|
|
} |
1487 |
|
✗ |
if (k > 0) *cur++ = scale * ((*in >> 4) ); |
1488 |
|
✗ |
} else if (depth == 2) { |
1489 |
|
✗ |
for (k=x*img_n; k >= 4; k-=4, ++in) { |
1490 |
|
✗ |
*cur++ = scale * ((*in >> 6) ); |
1491 |
|
✗ |
*cur++ = scale * ((*in >> 4) & 0x03); |
1492 |
|
✗ |
*cur++ = scale * ((*in >> 2) & 0x03); |
1493 |
|
✗ |
*cur++ = scale * ((*in ) & 0x03); |
1494 |
|
|
} |
1495 |
|
✗ |
if (k > 0) *cur++ = scale * ((*in >> 6) ); |
1496 |
|
✗ |
if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); |
1497 |
|
✗ |
if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); |
1498 |
|
✗ |
} else if (depth == 1) { |
1499 |
|
✗ |
for (k=x*img_n; k >= 8; k-=8, ++in) { |
1500 |
|
✗ |
*cur++ = scale * ((*in >> 7) ); |
1501 |
|
✗ |
*cur++ = scale * ((*in >> 6) & 0x01); |
1502 |
|
✗ |
*cur++ = scale * ((*in >> 5) & 0x01); |
1503 |
|
✗ |
*cur++ = scale * ((*in >> 4) & 0x01); |
1504 |
|
✗ |
*cur++ = scale * ((*in >> 3) & 0x01); |
1505 |
|
✗ |
*cur++ = scale * ((*in >> 2) & 0x01); |
1506 |
|
✗ |
*cur++ = scale * ((*in >> 1) & 0x01); |
1507 |
|
✗ |
*cur++ = scale * ((*in ) & 0x01); |
1508 |
|
|
} |
1509 |
|
✗ |
if (k > 0) *cur++ = scale * ((*in >> 7) ); |
1510 |
|
✗ |
if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); |
1511 |
|
✗ |
if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); |
1512 |
|
✗ |
if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); |
1513 |
|
✗ |
if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); |
1514 |
|
✗ |
if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); |
1515 |
|
✗ |
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); |
1516 |
|
|
} |
1517 |
|
✗ |
if (img_n != out_n) { |
1518 |
|
|
int q; |
1519 |
|
✗ |
cur = a->out + stride*j; |
1520 |
|
✗ |
if (img_n == 1) { |
1521 |
|
✗ |
for (q=x-1; q >= 0; --q) { |
1522 |
|
✗ |
cur[q*2+1] = 255; |
1523 |
|
✗ |
cur[q*2+0] = cur[q]; |
1524 |
|
|
} |
1525 |
|
|
} else { |
1526 |
|
|
STBI_ASSERT(img_n == 3); |
1527 |
|
✗ |
for (q=x-1; q >= 0; --q) { |
1528 |
|
✗ |
cur[q*4+3] = 255; |
1529 |
|
✗ |
cur[q*4+2] = cur[q*3+2]; |
1530 |
|
✗ |
cur[q*4+1] = cur[q*3+1]; |
1531 |
|
✗ |
cur[q*4+0] = cur[q*3+0]; |
1532 |
|
|
} |
1533 |
|
|
} |
1534 |
|
|
} |
1535 |
|
|
} |
1536 |
|
✗ |
} else if (depth == 16) { |
1537 |
|
✗ |
unsigned char *cur = a->out; |
1538 |
|
|
_m3dstbi__uint16 *cur16 = (_m3dstbi__uint16*)cur; |
1539 |
|
|
|
1540 |
|
✗ |
for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { |
1541 |
|
✗ |
*cur16 = (cur[0] << 8) | cur[1]; |
1542 |
|
|
} |
1543 |
|
|
} |
1544 |
|
|
|
1545 |
|
|
return 1; |
1546 |
|
|
} |
1547 |
|
|
|
1548 |
|
✗ |
static int _m3dstbi__create_png_image(_m3dstbi__png *a, unsigned char *image_data, _m3dstbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) |
1549 |
|
|
{ |
1550 |
|
✗ |
int bytes = (depth == 16 ? 2 : 1); |
1551 |
|
✗ |
int out_bytes = out_n * bytes; |
1552 |
|
|
unsigned char *final; |
1553 |
|
|
int p; |
1554 |
|
✗ |
if (!interlaced) |
1555 |
|
✗ |
return _m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); |
1556 |
|
|
|
1557 |
|
✗ |
final = (unsigned char *) _m3dstbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); |
1558 |
|
✗ |
for (p=0; p < 7; ++p) { |
1559 |
|
✗ |
int xorig[] = { 0,4,0,2,0,1,0 }; |
1560 |
|
✗ |
int yorig[] = { 0,0,4,0,2,0,1 }; |
1561 |
|
✗ |
int xspc[] = { 8,8,4,4,2,2,1 }; |
1562 |
|
✗ |
int yspc[] = { 8,8,8,4,4,2,2 }; |
1563 |
|
|
int i,j,x,y; |
1564 |
|
✗ |
x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; |
1565 |
|
✗ |
y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; |
1566 |
|
✗ |
if (x && y) { |
1567 |
|
✗ |
_m3dstbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; |
1568 |
|
✗ |
if (!_m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { |
1569 |
|
✗ |
STBI_FREE(final); |
1570 |
|
✗ |
return 0; |
1571 |
|
|
} |
1572 |
|
✗ |
for (j=0; j < y; ++j) { |
1573 |
|
✗ |
for (i=0; i < x; ++i) { |
1574 |
|
✗ |
int out_y = j*yspc[p]+yorig[p]; |
1575 |
|
✗ |
int out_x = i*xspc[p]+xorig[p]; |
1576 |
|
✗ |
memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, |
1577 |
|
✗ |
a->out + (j*x+i)*out_bytes, out_bytes); |
1578 |
|
|
} |
1579 |
|
|
} |
1580 |
|
✗ |
STBI_FREE(a->out); |
1581 |
|
✗ |
image_data += img_len; |
1582 |
|
✗ |
image_data_len -= img_len; |
1583 |
|
|
} |
1584 |
|
|
} |
1585 |
|
✗ |
a->out = final; |
1586 |
|
|
|
1587 |
|
✗ |
return 1; |
1588 |
|
|
} |
1589 |
|
|
|
1590 |
|
✗ |
static int _m3dstbi__compute_transparency(_m3dstbi__png *z, unsigned char tc[3], int out_n) |
1591 |
|
|
{ |
1592 |
|
✗ |
_m3dstbi__context *s = z->s; |
1593 |
|
✗ |
_m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; |
1594 |
|
✗ |
unsigned char *p = z->out; |
1595 |
|
|
|
1596 |
|
|
STBI_ASSERT(out_n == 2 || out_n == 4); |
1597 |
|
|
|
1598 |
|
✗ |
if (out_n == 2) { |
1599 |
|
✗ |
for (i=0; i < pixel_count; ++i) { |
1600 |
|
✗ |
p[1] = (p[0] == tc[0] ? 0 : 255); |
1601 |
|
✗ |
p += 2; |
1602 |
|
|
} |
1603 |
|
|
} else { |
1604 |
|
✗ |
for (i=0; i < pixel_count; ++i) { |
1605 |
|
✗ |
if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) |
1606 |
|
✗ |
p[3] = 0; |
1607 |
|
✗ |
p += 4; |
1608 |
|
|
} |
1609 |
|
|
} |
1610 |
|
✗ |
return 1; |
1611 |
|
|
} |
1612 |
|
|
|
1613 |
|
✗ |
static int _m3dstbi__compute_transparency16(_m3dstbi__png *z, _m3dstbi__uint16 tc[3], int out_n) |
1614 |
|
|
{ |
1615 |
|
✗ |
_m3dstbi__context *s = z->s; |
1616 |
|
✗ |
_m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; |
1617 |
|
✗ |
_m3dstbi__uint16 *p = (_m3dstbi__uint16*) z->out; |
1618 |
|
|
|
1619 |
|
|
STBI_ASSERT(out_n == 2 || out_n == 4); |
1620 |
|
|
|
1621 |
|
✗ |
if (out_n == 2) { |
1622 |
|
✗ |
for (i = 0; i < pixel_count; ++i) { |
1623 |
|
✗ |
p[1] = (p[0] == tc[0] ? 0 : 65535); |
1624 |
|
✗ |
p += 2; |
1625 |
|
|
} |
1626 |
|
|
} else { |
1627 |
|
✗ |
for (i = 0; i < pixel_count; ++i) { |
1628 |
|
✗ |
if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) |
1629 |
|
✗ |
p[3] = 0; |
1630 |
|
✗ |
p += 4; |
1631 |
|
|
} |
1632 |
|
|
} |
1633 |
|
✗ |
return 1; |
1634 |
|
|
} |
1635 |
|
|
|
1636 |
|
✗ |
static int _m3dstbi__expand_png_palette(_m3dstbi__png *a, unsigned char *palette, int len, int pal_img_n) |
1637 |
|
|
{ |
1638 |
|
✗ |
_m3dstbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; |
1639 |
|
✗ |
unsigned char *p, *temp_out, *orig = a->out; |
1640 |
|
|
|
1641 |
|
✗ |
p = (unsigned char *) _m3dstbi__malloc_mad2(pixel_count, pal_img_n, 0); |
1642 |
|
✗ |
if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); |
1643 |
|
|
|
1644 |
|
|
temp_out = p; |
1645 |
|
|
|
1646 |
|
✗ |
if (pal_img_n == 3) { |
1647 |
|
✗ |
for (i=0; i < pixel_count; ++i) { |
1648 |
|
✗ |
int n = orig[i]*4; |
1649 |
|
✗ |
p[0] = palette[n ]; |
1650 |
|
✗ |
p[1] = palette[n+1]; |
1651 |
|
✗ |
p[2] = palette[n+2]; |
1652 |
|
✗ |
p += 3; |
1653 |
|
|
} |
1654 |
|
|
} else { |
1655 |
|
✗ |
for (i=0; i < pixel_count; ++i) { |
1656 |
|
✗ |
int n = orig[i]*4; |
1657 |
|
✗ |
p[0] = palette[n ]; |
1658 |
|
✗ |
p[1] = palette[n+1]; |
1659 |
|
✗ |
p[2] = palette[n+2]; |
1660 |
|
✗ |
p[3] = palette[n+3]; |
1661 |
|
✗ |
p += 4; |
1662 |
|
|
} |
1663 |
|
|
} |
1664 |
|
✗ |
STBI_FREE(a->out); |
1665 |
|
✗ |
a->out = temp_out; |
1666 |
|
|
|
1667 |
|
|
STBI_NOTUSED(len); |
1668 |
|
|
|
1669 |
|
✗ |
return 1; |
1670 |
|
|
} |
1671 |
|
|
|
1672 |
|
|
#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) |
1673 |
|
|
|
1674 |
|
✗ |
static int _m3dstbi__parse_png_file(_m3dstbi__png *z, int scan, int req_comp) |
1675 |
|
|
{ |
1676 |
|
|
unsigned char palette[1024], pal_img_n=0; |
1677 |
|
|
unsigned char has_trans=0, tc[3]; |
1678 |
|
|
_m3dstbi__uint16 tc16[3]; |
1679 |
|
|
_m3dstbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; |
1680 |
|
|
int first=1,k,interlace=0, color=0; |
1681 |
|
✗ |
_m3dstbi__context *s = z->s; |
1682 |
|
|
|
1683 |
|
✗ |
z->expanded = NULL; |
1684 |
|
✗ |
z->idata = NULL; |
1685 |
|
✗ |
z->out = NULL; |
1686 |
|
|
|
1687 |
|
✗ |
if (!_m3dstbi__check_png_header(s)) return 0; |
1688 |
|
|
|
1689 |
|
✗ |
if (scan == STBI__SCAN_type) return 1; |
1690 |
|
|
|
1691 |
|
✗ |
for (;;) { |
1692 |
|
✗ |
_m3dstbi__pngchunk c = _m3dstbi__get_chunk_header(s); |
1693 |
|
✗ |
switch (c.type) { |
1694 |
|
✗ |
case STBI__PNG_TYPE('C','g','B','I'): |
1695 |
|
✗ |
_m3dstbi__skip(s, c.length); |
1696 |
|
|
break; |
1697 |
|
✗ |
case STBI__PNG_TYPE('I','H','D','R'): { |
1698 |
|
|
int comp,filter; |
1699 |
|
✗ |
if (!first) return _m3dstbi__err("multiple IHDR","Corrupt PNG"); |
1700 |
|
|
first = 0; |
1701 |
|
✗ |
if (c.length != 13) return _m3dstbi__err("bad IHDR len","Corrupt PNG"); |
1702 |
|
✗ |
s->img_x = _m3dstbi__get32be(s); if (s->img_x > (1 << 24)) return _m3dstbi__err("too large","Very large image (corrupt?)"); |
1703 |
|
✗ |
s->img_y = _m3dstbi__get32be(s); if (s->img_y > (1 << 24)) return _m3dstbi__err("too large","Very large image (corrupt?)"); |
1704 |
|
✗ |
z->depth = _m3dstbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return _m3dstbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); |
1705 |
|
✗ |
color = _m3dstbi__get8(s); if (color > 6) return _m3dstbi__err("bad ctype","Corrupt PNG"); |
1706 |
|
✗ |
if (color == 3 && z->depth == 16) return _m3dstbi__err("bad ctype","Corrupt PNG"); |
1707 |
|
✗ |
if (color == 3) pal_img_n = 3; else if (color & 1) return _m3dstbi__err("bad ctype","Corrupt PNG"); |
1708 |
|
✗ |
comp = _m3dstbi__get8(s); if (comp) return _m3dstbi__err("bad comp method","Corrupt PNG"); |
1709 |
|
✗ |
filter= _m3dstbi__get8(s); if (filter) return _m3dstbi__err("bad filter method","Corrupt PNG"); |
1710 |
|
✗ |
interlace = _m3dstbi__get8(s); if (interlace>1) return _m3dstbi__err("bad interlace method","Corrupt PNG"); |
1711 |
|
✗ |
if (!s->img_x || !s->img_y) return _m3dstbi__err("0-pixel image","Corrupt PNG"); |
1712 |
|
✗ |
if (!pal_img_n) { |
1713 |
|
✗ |
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); |
1714 |
|
✗ |
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return _m3dstbi__err("too large", "Image too large to decode"); |
1715 |
|
✗ |
if (scan == STBI__SCAN_header) return 1; |
1716 |
|
|
} else { |
1717 |
|
✗ |
s->img_n = 1; |
1718 |
|
✗ |
if ((1 << 30) / s->img_x / 4 < s->img_y) return _m3dstbi__err("too large","Corrupt PNG"); |
1719 |
|
|
} |
1720 |
|
|
break; |
1721 |
|
|
} |
1722 |
|
|
|
1723 |
|
✗ |
case STBI__PNG_TYPE('P','L','T','E'): { |
1724 |
|
✗ |
if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); |
1725 |
|
✗ |
if (c.length > 256*3) return _m3dstbi__err("invalid PLTE","Corrupt PNG"); |
1726 |
|
✗ |
pal_len = c.length / 3; |
1727 |
|
✗ |
if (pal_len * 3 != c.length) return _m3dstbi__err("invalid PLTE","Corrupt PNG"); |
1728 |
|
✗ |
for (i=0; i < pal_len; ++i) { |
1729 |
|
✗ |
palette[i*4+0] = _m3dstbi__get8(s); |
1730 |
|
✗ |
palette[i*4+1] = _m3dstbi__get8(s); |
1731 |
|
✗ |
palette[i*4+2] = _m3dstbi__get8(s); |
1732 |
|
✗ |
palette[i*4+3] = 255; |
1733 |
|
|
} |
1734 |
|
|
break; |
1735 |
|
|
} |
1736 |
|
|
|
1737 |
|
✗ |
case STBI__PNG_TYPE('t','R','N','S'): { |
1738 |
|
✗ |
if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); |
1739 |
|
✗ |
if (z->idata) return _m3dstbi__err("tRNS after IDAT","Corrupt PNG"); |
1740 |
|
✗ |
if (pal_img_n) { |
1741 |
|
✗ |
if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } |
1742 |
|
✗ |
if (pal_len == 0) return _m3dstbi__err("tRNS before PLTE","Corrupt PNG"); |
1743 |
|
✗ |
if (c.length > pal_len) return _m3dstbi__err("bad tRNS len","Corrupt PNG"); |
1744 |
|
|
pal_img_n = 4; |
1745 |
|
✗ |
for (i=0; i < c.length; ++i) |
1746 |
|
✗ |
palette[i*4+3] = _m3dstbi__get8(s); |
1747 |
|
|
} else { |
1748 |
|
✗ |
if (!(s->img_n & 1)) return _m3dstbi__err("tRNS with alpha","Corrupt PNG"); |
1749 |
|
✗ |
if (c.length != (_m3dstbi__uint32) s->img_n*2) return _m3dstbi__err("bad tRNS len","Corrupt PNG"); |
1750 |
|
|
has_trans = 1; |
1751 |
|
✗ |
if (z->depth == 16) { |
1752 |
|
✗ |
for (k = 0; k < s->img_n; ++k) tc16[k] = (_m3dstbi__uint16)_m3dstbi__get16be(s); |
1753 |
|
|
} else { |
1754 |
|
✗ |
for (k = 0; k < s->img_n; ++k) tc[k] = (unsigned char)(_m3dstbi__get16be(s) & 255) * _m3dstbi__depth_scale_table[z->depth]; |
1755 |
|
|
} |
1756 |
|
|
} |
1757 |
|
|
break; |
1758 |
|
|
} |
1759 |
|
|
|
1760 |
|
✗ |
case STBI__PNG_TYPE('I','D','A','T'): { |
1761 |
|
✗ |
if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); |
1762 |
|
✗ |
if (pal_img_n && !pal_len) return _m3dstbi__err("no PLTE","Corrupt PNG"); |
1763 |
|
✗ |
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } |
1764 |
|
✗ |
if ((int)(ioff + c.length) < (int)ioff) return 0; |
1765 |
|
✗ |
if (ioff + c.length > idata_limit) { |
1766 |
|
|
_m3dstbi__uint32 idata_limit_old = idata_limit; |
1767 |
|
|
unsigned char *p; |
1768 |
|
✗ |
if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; |
1769 |
|
✗ |
while (ioff + c.length > idata_limit) |
1770 |
|
✗ |
idata_limit *= 2; |
1771 |
|
|
STBI_NOTUSED(idata_limit_old); |
1772 |
|
✗ |
p = (unsigned char *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); |
1773 |
|
✗ |
z->idata = p; |
1774 |
|
|
} |
1775 |
|
✗ |
if (!_m3dstbi__getn(s, z->idata+ioff,c.length)) return _m3dstbi__err("outofdata","Corrupt PNG"); |
1776 |
|
|
ioff += c.length; |
1777 |
|
✗ |
break; |
1778 |
|
|
} |
1779 |
|
|
|
1780 |
|
✗ |
case STBI__PNG_TYPE('I','E','N','D'): { |
1781 |
|
|
_m3dstbi__uint32 raw_len, bpl; |
1782 |
|
✗ |
if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); |
1783 |
|
✗ |
if (scan != STBI__SCAN_load) return 1; |
1784 |
|
✗ |
if (z->idata == NULL) return _m3dstbi__err("no IDAT","Corrupt PNG"); |
1785 |
|
✗ |
bpl = (s->img_x * z->depth + 7) / 8; |
1786 |
|
✗ |
raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; |
1787 |
|
✗ |
z->expanded = (unsigned char *) _m3dstbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, 1); |
1788 |
|
✗ |
if (z->expanded == NULL) return 0; |
1789 |
|
✗ |
STBI_FREE(z->idata); z->idata = NULL; |
1790 |
|
✗ |
if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) |
1791 |
|
✗ |
s->img_out_n = s->img_n+1; |
1792 |
|
|
else |
1793 |
|
✗ |
s->img_out_n = s->img_n; |
1794 |
|
✗ |
if (!_m3dstbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; |
1795 |
|
✗ |
if (has_trans) { |
1796 |
|
✗ |
if (z->depth == 16) { |
1797 |
|
✗ |
if (!_m3dstbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; |
1798 |
|
|
} else { |
1799 |
|
✗ |
if (!_m3dstbi__compute_transparency(z, tc, s->img_out_n)) return 0; |
1800 |
|
|
} |
1801 |
|
|
} |
1802 |
|
✗ |
if (pal_img_n) { |
1803 |
|
✗ |
s->img_n = pal_img_n; |
1804 |
|
✗ |
s->img_out_n = pal_img_n; |
1805 |
|
✗ |
if (req_comp >= 3) s->img_out_n = req_comp; |
1806 |
|
✗ |
if (!_m3dstbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) |
1807 |
|
|
return 0; |
1808 |
|
✗ |
} else if (has_trans) { |
1809 |
|
✗ |
++s->img_n; |
1810 |
|
|
} |
1811 |
|
✗ |
STBI_FREE(z->expanded); z->expanded = NULL; |
1812 |
|
✗ |
return 1; |
1813 |
|
|
} |
1814 |
|
|
|
1815 |
|
✗ |
default: |
1816 |
|
✗ |
if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); |
1817 |
|
✗ |
if ((c.type & (1 << 29)) == 0) { |
1818 |
|
✗ |
return _m3dstbi__err("invalid_chunk", "PNG not supported: unknown PNG chunk type"); |
1819 |
|
|
} |
1820 |
|
✗ |
_m3dstbi__skip(s, c.length); |
1821 |
|
|
break; |
1822 |
|
|
} |
1823 |
|
✗ |
_m3dstbi__get32be(s); |
1824 |
|
|
} |
1825 |
|
|
} |
1826 |
|
|
|
1827 |
|
✗ |
static void *_m3dstbi__do_png(_m3dstbi__png *p, int *x, int *y, int *n, int req_comp, _m3dstbi__result_info *ri) |
1828 |
|
|
{ |
1829 |
|
|
void *result=NULL; |
1830 |
|
✗ |
if (req_comp < 0 || req_comp > 4) { _m3dstbi__err("bad req_comp", "Internal error"); return NULL; } |
1831 |
|
✗ |
if (_m3dstbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { |
1832 |
|
✗ |
if (p->depth < 8) |
1833 |
|
✗ |
ri->bits_per_channel = 8; |
1834 |
|
|
else |
1835 |
|
✗ |
ri->bits_per_channel = p->depth; |
1836 |
|
✗ |
result = p->out; |
1837 |
|
✗ |
p->out = NULL; |
1838 |
|
✗ |
if (req_comp && req_comp != p->s->img_out_n) { |
1839 |
|
✗ |
if (ri->bits_per_channel == 8) |
1840 |
|
✗ |
result = _m3dstbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); |
1841 |
|
|
else |
1842 |
|
✗ |
result = _m3dstbi__convert_format16((_m3dstbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); |
1843 |
|
✗ |
p->s->img_out_n = req_comp; |
1844 |
|
✗ |
if (result == NULL) return result; |
1845 |
|
|
} |
1846 |
|
✗ |
*x = p->s->img_x; |
1847 |
|
✗ |
*y = p->s->img_y; |
1848 |
|
✗ |
if (n) *n = p->s->img_n; |
1849 |
|
|
} |
1850 |
|
✗ |
STBI_FREE(p->out); p->out = NULL; |
1851 |
|
✗ |
STBI_FREE(p->expanded); p->expanded = NULL; |
1852 |
|
✗ |
STBI_FREE(p->idata); p->idata = NULL; |
1853 |
|
|
|
1854 |
|
✗ |
return result; |
1855 |
|
|
} |
1856 |
|
|
|
1857 |
|
|
static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp, int req_comp, _m3dstbi__result_info *ri) |
1858 |
|
|
{ |
1859 |
|
|
_m3dstbi__png p; |
1860 |
|
✗ |
p.s = s; |
1861 |
|
✗ |
return _m3dstbi__do_png(&p, x,y,comp,req_comp, ri); |
1862 |
|
|
} |
1863 |
|
|
#define stbi__context _m3dstbi__context |
1864 |
|
|
#define stbi__result_info _m3dstbi__result_info |
1865 |
|
|
#define stbi__png_load _m3dstbi__png_load |
1866 |
|
|
#define stbi_zlib_decode_malloc_guesssize_headerflag _m3dstbi_zlib_decode_malloc_guesssize_headerflag |
1867 |
|
|
#endif |
1868 |
|
|
#if !defined(M3D_NOIMPORTER) && defined(STBI_INCLUDE_STB_IMAGE_H) && !defined(STB_IMAGE_IMPLEMENTATION) |
1869 |
|
|
#error "stb_image.h included without STB_IMAGE_IMPLEMENTATION. Sorry, we need some stuff defined inside the ifguard for proper integration" |
1870 |
|
|
#endif |
1871 |
|
|
|
1872 |
|
|
#if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H) |
1873 |
|
|
/* zlib_compressor from |
1874 |
|
|
|
1875 |
|
|
stb_image_write - v1.13 - public domain - http://nothings.org/stb/stb_image_write.h |
1876 |
|
|
*/ |
1877 |
|
|
typedef unsigned char _m3dstbiw__uc; |
1878 |
|
|
typedef unsigned short _m3dstbiw__us; |
1879 |
|
|
|
1880 |
|
|
typedef uint16_t _m3dstbiw__uint16; |
1881 |
|
|
typedef int16_t _m3dstbiw__int16; |
1882 |
|
|
typedef uint32_t _m3dstbiw__uint32; |
1883 |
|
|
typedef int32_t _m3dstbiw__int32; |
1884 |
|
|
|
1885 |
|
|
#define STBIW_MALLOC(s) M3D_MALLOC(s) |
1886 |
|
|
#define STBIW_REALLOC(p,ns) M3D_REALLOC(p,ns) |
1887 |
|
|
#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) |
1888 |
|
|
#define STBIW_FREE M3D_FREE |
1889 |
|
|
#define STBIW_MEMMOVE memmove |
1890 |
|
|
#define STBIW_UCHAR (uint8_t) |
1891 |
|
|
#define STBIW_ASSERT(x) |
1892 |
|
|
#define _m3dstbiw___sbraw(a) ((int *) (a) - 2) |
1893 |
|
|
#define _m3dstbiw___sbm(a) _m3dstbiw___sbraw(a)[0] |
1894 |
|
|
#define _m3dstbiw___sbn(a) _m3dstbiw___sbraw(a)[1] |
1895 |
|
|
|
1896 |
|
|
#define _m3dstbiw___sbneedgrow(a,n) ((a)==0 || _m3dstbiw___sbn(a)+n >= _m3dstbiw___sbm(a)) |
1897 |
|
|
#define _m3dstbiw___sbmaybegrow(a,n) (_m3dstbiw___sbneedgrow(a,(n)) ? _m3dstbiw___sbgrow(a,n) : 0) |
1898 |
|
|
#define _m3dstbiw___sbgrow(a,n) _m3dstbiw___sbgrowf((void **) &(a), (n), sizeof(*(a))) |
1899 |
|
|
|
1900 |
|
|
#define _m3dstbiw___sbpush(a, v) (_m3dstbiw___sbmaybegrow(a,1), (a)[_m3dstbiw___sbn(a)++] = (v)) |
1901 |
|
|
#define _m3dstbiw___sbcount(a) ((a) ? _m3dstbiw___sbn(a) : 0) |
1902 |
|
|
#define _m3dstbiw___sbfree(a) ((a) ? STBIW_FREE(_m3dstbiw___sbraw(a)),0 : 0) |
1903 |
|
|
|
1904 |
|
|
static void *_m3dstbiw___sbgrowf(void **arr, int increment, int itemsize) |
1905 |
|
|
{ |
1906 |
|
|
int m = *arr ? 2*_m3dstbiw___sbm(*arr)+increment : increment+1; |
1907 |
|
|
void *p = STBIW_REALLOC_SIZED(*arr ? _m3dstbiw___sbraw(*arr) : 0, *arr ? (_m3dstbiw___sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); |
1908 |
|
|
STBIW_ASSERT(p); |
1909 |
|
|
if (p) { |
1910 |
|
|
if (!*arr) ((int *) p)[1] = 0; |
1911 |
|
|
*arr = (void *) ((int *) p + 2); |
1912 |
|
|
_m3dstbiw___sbm(*arr) = m; |
1913 |
|
|
} |
1914 |
|
|
return *arr; |
1915 |
|
|
} |
1916 |
|
|
|
1917 |
|
|
static unsigned char *_m3dstbiw___zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) |
1918 |
|
|
{ |
1919 |
|
|
while (*bitcount >= 8) { |
1920 |
|
|
_m3dstbiw___sbpush(data, STBIW_UCHAR(*bitbuffer)); |
1921 |
|
|
*bitbuffer >>= 8; |
1922 |
|
|
*bitcount -= 8; |
1923 |
|
|
} |
1924 |
|
|
return data; |
1925 |
|
|
} |
1926 |
|
|
|
1927 |
|
|
static int _m3dstbiw___zlib_bitrev(int code, int codebits) |
1928 |
|
|
{ |
1929 |
|
|
int res=0; |
1930 |
|
|
while (codebits--) { |
1931 |
|
|
res = (res << 1) | (code & 1); |
1932 |
|
|
code >>= 1; |
1933 |
|
|
} |
1934 |
|
|
return res; |
1935 |
|
|
} |
1936 |
|
|
|
1937 |
|
|
static unsigned int _m3dstbiw___zlib_countm(unsigned char *a, unsigned char *b, int limit) |
1938 |
|
|
{ |
1939 |
|
|
int i; |
1940 |
|
|
for (i=0; i < limit && i < 258; ++i) |
1941 |
|
|
if (a[i] != b[i]) break; |
1942 |
|
|
return i; |
1943 |
|
|
} |
1944 |
|
|
|
1945 |
|
|
static unsigned int _m3dstbiw___zhash(unsigned char *data) |
1946 |
|
|
{ |
1947 |
|
|
_m3dstbiw__uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); |
1948 |
|
|
hash ^= hash << 3; |
1949 |
|
|
hash += hash >> 5; |
1950 |
|
|
hash ^= hash << 4; |
1951 |
|
|
hash += hash >> 17; |
1952 |
|
|
hash ^= hash << 25; |
1953 |
|
|
hash += hash >> 6; |
1954 |
|
|
return hash; |
1955 |
|
|
} |
1956 |
|
|
|
1957 |
|
|
#define _m3dstbiw___zlib_flush() (out = _m3dstbiw___zlib_flushf(out, &bitbuf, &bitcount)) |
1958 |
|
|
#define _m3dstbiw___zlib_add(code,codebits) \ |
1959 |
|
|
(bitbuf |= (code) << bitcount, bitcount += (codebits), _m3dstbiw___zlib_flush()) |
1960 |
|
|
#define _m3dstbiw___zlib_huffa(b,c) _m3dstbiw___zlib_add(_m3dstbiw___zlib_bitrev(b,c),c) |
1961 |
|
|
#define _m3dstbiw___zlib_huff1(n) _m3dstbiw___zlib_huffa(0x30 + (n), 8) |
1962 |
|
|
#define _m3dstbiw___zlib_huff2(n) _m3dstbiw___zlib_huffa(0x190 + (n)-144, 9) |
1963 |
|
|
#define _m3dstbiw___zlib_huff3(n) _m3dstbiw___zlib_huffa(0 + (n)-256,7) |
1964 |
|
|
#define _m3dstbiw___zlib_huff4(n) _m3dstbiw___zlib_huffa(0xc0 + (n)-280,8) |
1965 |
|
|
#define _m3dstbiw___zlib_huff(n) ((n) <= 143 ? _m3dstbiw___zlib_huff1(n) : (n) <= 255 ? _m3dstbiw___zlib_huff2(n) : (n) <= 279 ? _m3dstbiw___zlib_huff3(n) : _m3dstbiw___zlib_huff4(n)) |
1966 |
|
|
#define _m3dstbiw___zlib_huffb(n) ((n) <= 143 ? _m3dstbiw___zlib_huff1(n) : _m3dstbiw___zlib_huff2(n)) |
1967 |
|
|
|
1968 |
|
|
#define _m3dstbiw___ZHASH 16384 |
1969 |
|
|
|
1970 |
|
|
unsigned char * _m3dstbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) |
1971 |
|
|
{ |
1972 |
|
|
static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; |
1973 |
|
|
static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; |
1974 |
|
|
static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; |
1975 |
|
|
static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; |
1976 |
|
|
unsigned int bitbuf=0; |
1977 |
|
|
int i,j, bitcount=0; |
1978 |
|
|
unsigned char *out = NULL; |
1979 |
|
|
unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(_m3dstbiw___ZHASH * sizeof(char**)); |
1980 |
|
|
if (hash_table == NULL) |
1981 |
|
|
return NULL; |
1982 |
|
|
if (quality < 5) quality = 5; |
1983 |
|
|
|
1984 |
|
|
_m3dstbiw___sbpush(out, 0x78); |
1985 |
|
|
_m3dstbiw___sbpush(out, 0x5e); |
1986 |
|
|
_m3dstbiw___zlib_add(1,1); |
1987 |
|
|
_m3dstbiw___zlib_add(1,2); |
1988 |
|
|
|
1989 |
|
|
for (i=0; i < _m3dstbiw___ZHASH; ++i) |
1990 |
|
|
hash_table[i] = NULL; |
1991 |
|
|
|
1992 |
|
|
i=0; |
1993 |
|
|
while (i < data_len-3) { |
1994 |
|
|
int h = _m3dstbiw___zhash(data+i)&(_m3dstbiw___ZHASH-1), best=3; |
1995 |
|
|
unsigned char *bestloc = 0; |
1996 |
|
|
unsigned char **hlist = hash_table[h]; |
1997 |
|
|
int n = _m3dstbiw___sbcount(hlist); |
1998 |
|
|
for (j=0; j < n; ++j) { |
1999 |
|
|
if (hlist[j]-data > i-32768) { |
2000 |
|
|
int d = _m3dstbiw___zlib_countm(hlist[j], data+i, data_len-i); |
2001 |
|
|
if (d >= best) best=d,bestloc=hlist[j]; |
2002 |
|
|
} |
2003 |
|
|
} |
2004 |
|
|
if (hash_table[h] && _m3dstbiw___sbn(hash_table[h]) == 2*quality) { |
2005 |
|
|
STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); |
2006 |
|
|
_m3dstbiw___sbn(hash_table[h]) = quality; |
2007 |
|
|
} |
2008 |
|
|
_m3dstbiw___sbpush(hash_table[h],data+i); |
2009 |
|
|
|
2010 |
|
|
if (bestloc) { |
2011 |
|
|
h = _m3dstbiw___zhash(data+i+1)&(_m3dstbiw___ZHASH-1); |
2012 |
|
|
hlist = hash_table[h]; |
2013 |
|
|
n = _m3dstbiw___sbcount(hlist); |
2014 |
|
|
for (j=0; j < n; ++j) { |
2015 |
|
|
if (hlist[j]-data > i-32767) { |
2016 |
|
|
int e = _m3dstbiw___zlib_countm(hlist[j], data+i+1, data_len-i-1); |
2017 |
|
|
if (e > best) { |
2018 |
|
|
bestloc = NULL; |
2019 |
|
|
break; |
2020 |
|
|
} |
2021 |
|
|
} |
2022 |
|
|
} |
2023 |
|
|
} |
2024 |
|
|
|
2025 |
|
|
if (bestloc) { |
2026 |
|
|
int d = (int) (data+i - bestloc); |
2027 |
|
|
STBIW_ASSERT(d <= 32767 && best <= 258); |
2028 |
|
|
for (j=0; best > lengthc[j+1]-1; ++j); |
2029 |
|
|
_m3dstbiw___zlib_huff(j+257); |
2030 |
|
|
if (lengtheb[j]) _m3dstbiw___zlib_add(best - lengthc[j], lengtheb[j]); |
2031 |
|
|
for (j=0; d > distc[j+1]-1; ++j); |
2032 |
|
|
_m3dstbiw___zlib_add(_m3dstbiw___zlib_bitrev(j,5),5); |
2033 |
|
|
if (disteb[j]) _m3dstbiw___zlib_add(d - distc[j], disteb[j]); |
2034 |
|
|
i += best; |
2035 |
|
|
} else { |
2036 |
|
|
_m3dstbiw___zlib_huffb(data[i]); |
2037 |
|
|
++i; |
2038 |
|
|
} |
2039 |
|
|
} |
2040 |
|
|
for (;i < data_len; ++i) |
2041 |
|
|
_m3dstbiw___zlib_huffb(data[i]); |
2042 |
|
|
_m3dstbiw___zlib_huff(256); |
2043 |
|
|
while (bitcount) |
2044 |
|
|
_m3dstbiw___zlib_add(0,1); |
2045 |
|
|
|
2046 |
|
|
for (i=0; i < _m3dstbiw___ZHASH; ++i) |
2047 |
|
|
(void) _m3dstbiw___sbfree(hash_table[i]); |
2048 |
|
|
STBIW_FREE(hash_table); |
2049 |
|
|
|
2050 |
|
|
{ |
2051 |
|
|
unsigned int s1=1, s2=0; |
2052 |
|
|
int blocklen = (int) (data_len % 5552); |
2053 |
|
|
j=0; |
2054 |
|
|
while (j < data_len) { |
2055 |
|
|
for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; |
2056 |
|
|
s1 %= 65521, s2 %= 65521; |
2057 |
|
|
j += blocklen; |
2058 |
|
|
blocklen = 5552; |
2059 |
|
|
} |
2060 |
|
|
_m3dstbiw___sbpush(out, STBIW_UCHAR(s2 >> 8)); |
2061 |
|
|
_m3dstbiw___sbpush(out, STBIW_UCHAR(s2)); |
2062 |
|
|
_m3dstbiw___sbpush(out, STBIW_UCHAR(s1 >> 8)); |
2063 |
|
|
_m3dstbiw___sbpush(out, STBIW_UCHAR(s1)); |
2064 |
|
|
} |
2065 |
|
|
*out_len = _m3dstbiw___sbn(out); |
2066 |
|
|
STBIW_MEMMOVE(_m3dstbiw___sbraw(out), out, *out_len); |
2067 |
|
|
return (unsigned char *) _m3dstbiw___sbraw(out); |
2068 |
|
|
} |
2069 |
|
|
#define stbi_zlib_compress _m3dstbi_zlib_compress |
2070 |
|
|
#else |
2071 |
|
|
unsigned char * _m3dstbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality); |
2072 |
|
|
#endif |
2073 |
|
|
|
2074 |
|
|
#define M3D_CHUNKMAGIC(m, a,b,c,d) ((m)[0]==(a) && (m)[1]==(b) && (m)[2]==(c) && (m)[3]==(d)) |
2075 |
|
|
|
2076 |
|
|
#ifdef M3D_ASCII |
2077 |
|
|
#include <stdio.h> /* get sprintf */ |
2078 |
|
|
#include <locale.h> /* sprintf and strtod cares about number locale */ |
2079 |
|
|
#endif |
2080 |
|
|
#ifdef M3D_PROFILING |
2081 |
|
|
#include <sys/time.h> |
2082 |
|
|
#endif |
2083 |
|
|
|
2084 |
|
|
#if !defined(M3D_NOIMPORTER) && defined(M3D_ASCII) |
2085 |
|
|
/* helper functions for the ASCII parser */ |
2086 |
|
|
static char *_m3d_findarg(char *s) { |
2087 |
|
|
while(s && *s && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n') s++; |
2088 |
|
|
while(s && *s && (*s == ' ' || *s == '\t')) s++; |
2089 |
|
|
return s; |
2090 |
|
|
} |
2091 |
|
|
static char *_m3d_findnl(char *s) { |
2092 |
|
|
while(s && *s && *s != '\r' && *s != '\n') s++; |
2093 |
|
|
if(*s == '\r') s++; |
2094 |
|
|
if(*s == '\n') s++; |
2095 |
|
|
return s; |
2096 |
|
|
} |
2097 |
|
|
static char *_m3d_gethex(char *s, uint32_t *ret) |
2098 |
|
|
{ |
2099 |
|
|
if(*s == '#') s++; |
2100 |
|
|
*ret = 0; |
2101 |
|
|
for(; *s; s++) { |
2102 |
|
|
if(*s >= '0' && *s <= '9') { *ret <<= 4; *ret += (uint32_t)(*s-'0'); } |
2103 |
|
|
else if(*s >= 'a' && *s <= 'f') { *ret <<= 4; *ret += (uint32_t)(*s-'a'+10); } |
2104 |
|
|
else if(*s >= 'A' && *s <= 'F') { *ret <<= 4; *ret += (uint32_t)(*s-'A'+10); } |
2105 |
|
|
else break; |
2106 |
|
|
} |
2107 |
|
|
return _m3d_findarg(s); |
2108 |
|
|
} |
2109 |
|
|
static char *_m3d_getint(char *s, uint32_t *ret) |
2110 |
|
|
{ |
2111 |
|
|
char *e = s; |
2112 |
|
|
if(!s || !*s || *s == '\r' || *s == '\n') return s; |
2113 |
|
|
for(; *e >= '0' && *e <= '9'; e++); |
2114 |
|
|
*ret = atoi(s); |
2115 |
|
|
return e; |
2116 |
|
|
} |
2117 |
|
|
static char *_m3d_getfloat(char *s, M3D_FLOAT *ret) |
2118 |
|
|
{ |
2119 |
|
|
char *e = s; |
2120 |
|
|
if(!s || !*s || *s == '\r' || *s == '\n') return s; |
2121 |
|
|
for(; *e == '-' || *e == '+' || *e == '.' || (*e >= '0' && *e <= '9') || *e == 'e' || *e == 'E'; e++); |
2122 |
|
|
*ret = (M3D_FLOAT)strtod(s, NULL); |
2123 |
|
|
return _m3d_findarg(e); |
2124 |
|
|
} |
2125 |
|
|
#endif |
2126 |
|
|
#if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_ASCII) || defined(M3D_EXPORTER)) |
2127 |
|
|
/* helper function to create safe strings */ |
2128 |
|
✗ |
char *_m3d_safestr(char *in, int morelines) |
2129 |
|
|
{ |
2130 |
|
|
char *out, *o, *i = in; |
2131 |
|
|
int l; |
2132 |
|
✗ |
if(!in || !*in) { |
2133 |
|
✗ |
out = (char*)M3D_MALLOC(1); |
2134 |
|
✗ |
if(!out) return NULL; |
2135 |
|
✗ |
out[0] =0; |
2136 |
|
|
} else { |
2137 |
|
✗ |
for(o = in, l = 0; *o && ((morelines & 1) || (*o != '\r' && *o != '\n')) && l < 256; o++, l++); |
2138 |
|
✗ |
out = o = (char*)M3D_MALLOC(l+1); |
2139 |
|
✗ |
if(!out) return NULL; |
2140 |
|
✗ |
while(*i == ' ' || *i == '\t' || *i == '\r' || (morelines && *i == '\n')) i++; |
2141 |
|
✗ |
for(; *i && (morelines || (*i != '\r' && *i != '\n')); i++) { |
2142 |
|
✗ |
if(*i == '\r') continue; |
2143 |
|
✗ |
if(*i == '\n') { |
2144 |
|
✗ |
if(morelines >= 3 && o > out && *(o-1) == '\n') break; |
2145 |
|
✗ |
if(i > in && *(i-1) == '\n') continue; |
2146 |
|
✗ |
if(morelines & 1) { |
2147 |
|
✗ |
if(morelines == 1) *o++ = '\r'; |
2148 |
|
✗ |
*o++ = '\n'; |
2149 |
|
|
} else |
2150 |
|
|
break; |
2151 |
|
|
} else |
2152 |
|
✗ |
if(*i == ' ' || *i == '\t') { |
2153 |
|
✗ |
*o++ = morelines? ' ' : '_'; |
2154 |
|
|
} else |
2155 |
|
✗ |
*o++ = !morelines && (*i == '/' || *i == '\\') ? '_' : *i; |
2156 |
|
|
} |
2157 |
|
✗ |
for(; o > out && (*(o-1) == ' ' || *(o-1) == '\t' || *(o-1) == '\r' || *(o-1) == '\n'); o--); |
2158 |
|
✗ |
*o = 0; |
2159 |
|
✗ |
out = (char*)M3D_REALLOC(out, (uintptr_t)o - (uintptr_t)out + 1); |
2160 |
|
|
} |
2161 |
|
|
return out; |
2162 |
|
|
} |
2163 |
|
|
#endif |
2164 |
|
|
#ifndef M3D_NOIMPORTER |
2165 |
|
|
/* helper function to load and decode/generate a texture */ |
2166 |
|
✗ |
M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char *fn) |
2167 |
|
|
{ |
2168 |
|
✗ |
unsigned int i, len = 0; |
2169 |
|
|
unsigned char *buff = NULL; |
2170 |
|
|
char *fn2; |
2171 |
|
|
unsigned int w, h; |
2172 |
|
|
stbi__context s; |
2173 |
|
|
stbi__result_info ri; |
2174 |
|
|
|
2175 |
|
|
/* do we have loaded this texture already? */ |
2176 |
|
✗ |
for(i = 0; i < model->numtexture; i++) |
2177 |
|
✗ |
if(!strcmp(fn, model->texture[i].name)) return i; |
2178 |
|
|
/* see if it's inlined in the model */ |
2179 |
|
✗ |
if(model->inlined) { |
2180 |
|
✗ |
for(i = 0; i < model->numinlined; i++) |
2181 |
|
✗ |
if(!strcmp(fn, model->inlined[i].name)) { |
2182 |
|
✗ |
buff = model->inlined[i].data; |
2183 |
|
✗ |
len = model->inlined[i].length; |
2184 |
|
|
freecb = NULL; |
2185 |
|
✗ |
break; |
2186 |
|
|
} |
2187 |
|
|
} |
2188 |
|
|
/* try to load from external source */ |
2189 |
|
✗ |
if(!buff && readfilecb) { |
2190 |
|
✗ |
i = (unsigned int)strlen(fn); |
2191 |
|
✗ |
if(i < 5 || fn[i - 4] != '.') { |
2192 |
|
✗ |
fn2 = (char*)M3D_MALLOC(i + 5); |
2193 |
|
✗ |
if(!fn2) { model->errcode = M3D_ERR_ALLOC; return M3D_UNDEF; } |
2194 |
|
|
memcpy(fn2, fn, i); |
2195 |
|
✗ |
memcpy(fn2+i, ".png", 5); |
2196 |
|
✗ |
buff = (*readfilecb)(fn2, &len); |
2197 |
|
✗ |
M3D_FREE(fn2); |
2198 |
|
|
} |
2199 |
|
✗ |
if(!buff) { |
2200 |
|
✗ |
buff = (*readfilecb)(fn, &len); |
2201 |
|
✗ |
if(!buff) return M3D_UNDEF; |
2202 |
|
|
} |
2203 |
|
|
} |
2204 |
|
|
/* add to textures array */ |
2205 |
|
✗ |
i = model->numtexture++; |
2206 |
|
✗ |
model->texture = (m3dtx_t*)M3D_REALLOC(model->texture, model->numtexture * sizeof(m3dtx_t)); |
2207 |
|
✗ |
if(!model->texture) { |
2208 |
|
✗ |
if(buff && freecb) (*freecb)(buff); |
2209 |
|
✗ |
model->errcode = M3D_ERR_ALLOC; |
2210 |
|
✗ |
return M3D_UNDEF; |
2211 |
|
|
} |
2212 |
|
✗ |
model->texture[i].name = fn; |
2213 |
|
✗ |
model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL; |
2214 |
|
✗ |
if(buff) { |
2215 |
|
✗ |
if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') { |
2216 |
|
✗ |
s.read_from_callbacks = 0; |
2217 |
|
✗ |
s.img_buffer = s.img_buffer_original = (unsigned char *) buff; |
2218 |
|
✗ |
s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len; |
2219 |
|
|
/* don't use model->texture[i].w directly, it's a uint16_t */ |
2220 |
|
✗ |
w = h = len = 0; |
2221 |
|
✗ |
ri.bits_per_channel = 8; |
2222 |
|
✗ |
model->texture[i].d = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&len, 0, &ri); |
2223 |
|
✗ |
model->texture[i].w = w; |
2224 |
|
✗ |
model->texture[i].h = h; |
2225 |
|
✗ |
model->texture[i].f = (uint8_t)len; |
2226 |
|
|
} else { |
2227 |
|
|
#ifdef M3D_TX_INTERP |
2228 |
|
|
if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) { |
2229 |
|
|
M3D_LOG("Unable to generate texture"); |
2230 |
|
|
M3D_LOG(fn); |
2231 |
|
|
} |
2232 |
|
|
#else |
2233 |
|
|
M3D_LOG("Unimplemented interpreter"); |
2234 |
|
|
M3D_LOG(fn); |
2235 |
|
|
#endif |
2236 |
|
|
} |
2237 |
|
✗ |
if(freecb) (*freecb)(buff); |
2238 |
|
|
} |
2239 |
|
✗ |
if(!model->texture[i].d) |
2240 |
|
✗ |
model->errcode = M3D_ERR_UNKIMG; |
2241 |
|
|
return i; |
2242 |
|
|
} |
2243 |
|
|
|
2244 |
|
|
/* helper function to load and generate a procedural surface */ |
2245 |
|
✗ |
void _m3d_getpr(m3d_t *model, _unused m3dread_t readfilecb, _unused m3dfree_t freecb, _unused char *fn) |
2246 |
|
|
{ |
2247 |
|
|
#ifdef M3D_PR_INTERP |
2248 |
|
|
unsigned int i, len = 0; |
2249 |
|
|
unsigned char *buff = readfilecb ? (*readfilecb)(fn, &len) : NULL; |
2250 |
|
|
|
2251 |
|
|
if(!buff && model->inlined) { |
2252 |
|
|
for(i = 0; i < model->numinlined; i++) |
2253 |
|
|
if(!strcmp(fn, model->inlined[i].name)) { |
2254 |
|
|
buff = model->inlined[i].data; |
2255 |
|
|
len = model->inlined[i].length; |
2256 |
|
|
freecb = NULL; |
2257 |
|
|
break; |
2258 |
|
|
} |
2259 |
|
|
} |
2260 |
|
|
if(!buff || !len || (model->errcode = M3D_PR_INTERP(fn, buff, len, model)) != M3D_SUCCESS) { |
2261 |
|
|
M3D_LOG("Unable to generate procedural surface"); |
2262 |
|
|
M3D_LOG(fn); |
2263 |
|
|
model->errcode = M3D_ERR_UNKIMG; |
2264 |
|
|
} |
2265 |
|
|
if(freecb && buff) (*freecb)(buff); |
2266 |
|
|
#else |
2267 |
|
|
(void)readfilecb; |
2268 |
|
|
(void)freecb; |
2269 |
|
|
(void)fn; |
2270 |
|
|
M3D_LOG("Unimplemented interpreter"); |
2271 |
|
|
M3D_LOG(fn); |
2272 |
|
✗ |
model->errcode = M3D_ERR_UNIMPL; |
2273 |
|
|
#endif |
2274 |
|
|
} |
2275 |
|
|
/* helpers to read indices from data stream */ |
2276 |
|
|
#define M3D_GETSTR(x) do{offs=0;data=_m3d_getidx(data,model->si_s,&offs);x=offs?((char*)model->raw+16+offs):NULL;}while(0) |
2277 |
|
|
_inline static unsigned char *_m3d_getidx(unsigned char *data, char type, M3D_INDEX *idx) |
2278 |
|
|
{ |
2279 |
|
✗ |
switch(type) { |
2280 |
|
✗ |
case 1: *idx = data[0] > 253 ? (int8_t)data[0] : data[0]; data++; break; |
2281 |
|
✗ |
case 2: *idx = *((uint16_t*)data) > 65533 ? *((int16_t*)data) : *((uint16_t*)data); data += 2; break; |
2282 |
|
✗ |
case 4: *idx = *((int32_t*)data); data += 4; break; |
2283 |
|
|
} |
2284 |
|
|
return data; |
2285 |
|
|
} |
2286 |
|
|
|
2287 |
|
|
#ifndef M3D_NOANIMATION |
2288 |
|
|
/* multiply 4 x 4 matrices. Do not use float *r[16] as argument, because some compilers misinterpret that as |
2289 |
|
|
* 16 pointers each pointing to a float, but we need a single pointer to 16 floats. */ |
2290 |
|
✗ |
void _m3d_mul(M3D_FLOAT *r, M3D_FLOAT *a, M3D_FLOAT *b) |
2291 |
|
|
{ |
2292 |
|
✗ |
r[ 0] = b[ 0] * a[ 0] + b[ 4] * a[ 1] + b[ 8] * a[ 2] + b[12] * a[ 3]; |
2293 |
|
✗ |
r[ 1] = b[ 1] * a[ 0] + b[ 5] * a[ 1] + b[ 9] * a[ 2] + b[13] * a[ 3]; |
2294 |
|
✗ |
r[ 2] = b[ 2] * a[ 0] + b[ 6] * a[ 1] + b[10] * a[ 2] + b[14] * a[ 3]; |
2295 |
|
✗ |
r[ 3] = b[ 3] * a[ 0] + b[ 7] * a[ 1] + b[11] * a[ 2] + b[15] * a[ 3]; |
2296 |
|
✗ |
r[ 4] = b[ 0] * a[ 4] + b[ 4] * a[ 5] + b[ 8] * a[ 6] + b[12] * a[ 7]; |
2297 |
|
✗ |
r[ 5] = b[ 1] * a[ 4] + b[ 5] * a[ 5] + b[ 9] * a[ 6] + b[13] * a[ 7]; |
2298 |
|
✗ |
r[ 6] = b[ 2] * a[ 4] + b[ 6] * a[ 5] + b[10] * a[ 6] + b[14] * a[ 7]; |
2299 |
|
✗ |
r[ 7] = b[ 3] * a[ 4] + b[ 7] * a[ 5] + b[11] * a[ 6] + b[15] * a[ 7]; |
2300 |
|
✗ |
r[ 8] = b[ 0] * a[ 8] + b[ 4] * a[ 9] + b[ 8] * a[10] + b[12] * a[11]; |
2301 |
|
✗ |
r[ 9] = b[ 1] * a[ 8] + b[ 5] * a[ 9] + b[ 9] * a[10] + b[13] * a[11]; |
2302 |
|
✗ |
r[10] = b[ 2] * a[ 8] + b[ 6] * a[ 9] + b[10] * a[10] + b[14] * a[11]; |
2303 |
|
✗ |
r[11] = b[ 3] * a[ 8] + b[ 7] * a[ 9] + b[11] * a[10] + b[15] * a[11]; |
2304 |
|
✗ |
r[12] = b[ 0] * a[12] + b[ 4] * a[13] + b[ 8] * a[14] + b[12] * a[15]; |
2305 |
|
✗ |
r[13] = b[ 1] * a[12] + b[ 5] * a[13] + b[ 9] * a[14] + b[13] * a[15]; |
2306 |
|
✗ |
r[14] = b[ 2] * a[12] + b[ 6] * a[13] + b[10] * a[14] + b[14] * a[15]; |
2307 |
|
✗ |
r[15] = b[ 3] * a[12] + b[ 7] * a[13] + b[11] * a[14] + b[15] * a[15]; |
2308 |
|
|
} |
2309 |
|
|
/* calculate 4 x 4 matrix inverse */ |
2310 |
|
✗ |
void _m3d_inv(M3D_FLOAT *m) |
2311 |
|
|
{ |
2312 |
|
|
M3D_FLOAT r[16]; |
2313 |
|
✗ |
M3D_FLOAT det = |
2314 |
|
✗ |
m[ 0]*m[ 5]*m[10]*m[15] - m[ 0]*m[ 5]*m[11]*m[14] + m[ 0]*m[ 6]*m[11]*m[13] - m[ 0]*m[ 6]*m[ 9]*m[15] |
2315 |
|
✗ |
+ m[ 0]*m[ 7]*m[ 9]*m[14] - m[ 0]*m[ 7]*m[10]*m[13] - m[ 1]*m[ 6]*m[11]*m[12] + m[ 1]*m[ 6]*m[ 8]*m[15] |
2316 |
|
✗ |
- m[ 1]*m[ 7]*m[ 8]*m[14] + m[ 1]*m[ 7]*m[10]*m[12] - m[ 1]*m[ 4]*m[10]*m[15] + m[ 1]*m[ 4]*m[11]*m[14] |
2317 |
|
✗ |
+ m[ 2]*m[ 7]*m[ 8]*m[13] - m[ 2]*m[ 7]*m[ 9]*m[12] + m[ 2]*m[ 4]*m[ 9]*m[15] - m[ 2]*m[ 4]*m[11]*m[13] |
2318 |
|
✗ |
+ m[ 2]*m[ 5]*m[11]*m[12] - m[ 2]*m[ 5]*m[ 8]*m[15] - m[ 3]*m[ 4]*m[ 9]*m[14] + m[ 3]*m[ 4]*m[10]*m[13] |
2319 |
|
✗ |
- m[ 3]*m[ 5]*m[10]*m[12] + m[ 3]*m[ 5]*m[ 8]*m[14] - m[ 3]*m[ 6]*m[ 8]*m[13] + m[ 3]*m[ 6]*m[ 9]*m[12]; |
2320 |
|
✗ |
if(det == (M3D_FLOAT)0.0 || det == (M3D_FLOAT)-0.0) det = (M3D_FLOAT)1.0; else det = (M3D_FLOAT)1.0 / det; |
2321 |
|
✗ |
r[ 0] = det *(m[ 5]*(m[10]*m[15] - m[11]*m[14]) + m[ 6]*(m[11]*m[13] - m[ 9]*m[15]) + m[ 7]*(m[ 9]*m[14] - m[10]*m[13])); |
2322 |
|
✗ |
r[ 1] = -det*(m[ 1]*(m[10]*m[15] - m[11]*m[14]) + m[ 2]*(m[11]*m[13] - m[ 9]*m[15]) + m[ 3]*(m[ 9]*m[14] - m[10]*m[13])); |
2323 |
|
✗ |
r[ 2] = det *(m[ 1]*(m[ 6]*m[15] - m[ 7]*m[14]) + m[ 2]*(m[ 7]*m[13] - m[ 5]*m[15]) + m[ 3]*(m[ 5]*m[14] - m[ 6]*m[13])); |
2324 |
|
✗ |
r[ 3] = -det*(m[ 1]*(m[ 6]*m[11] - m[ 7]*m[10]) + m[ 2]*(m[ 7]*m[ 9] - m[ 5]*m[11]) + m[ 3]*(m[ 5]*m[10] - m[ 6]*m[ 9])); |
2325 |
|
✗ |
r[ 4] = -det*(m[ 4]*(m[10]*m[15] - m[11]*m[14]) + m[ 6]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 7]*(m[ 8]*m[14] - m[10]*m[12])); |
2326 |
|
✗ |
r[ 5] = det *(m[ 0]*(m[10]*m[15] - m[11]*m[14]) + m[ 2]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 3]*(m[ 8]*m[14] - m[10]*m[12])); |
2327 |
|
✗ |
r[ 6] = -det*(m[ 0]*(m[ 6]*m[15] - m[ 7]*m[14]) + m[ 2]*(m[ 7]*m[12] - m[ 4]*m[15]) + m[ 3]*(m[ 4]*m[14] - m[ 6]*m[12])); |
2328 |
|
✗ |
r[ 7] = det *(m[ 0]*(m[ 6]*m[11] - m[ 7]*m[10]) + m[ 2]*(m[ 7]*m[ 8] - m[ 4]*m[11]) + m[ 3]*(m[ 4]*m[10] - m[ 6]*m[ 8])); |
2329 |
|
✗ |
r[ 8] = det *(m[ 4]*(m[ 9]*m[15] - m[11]*m[13]) + m[ 5]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 7]*(m[ 8]*m[13] - m[ 9]*m[12])); |
2330 |
|
✗ |
r[ 9] = -det*(m[ 0]*(m[ 9]*m[15] - m[11]*m[13]) + m[ 1]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 3]*(m[ 8]*m[13] - m[ 9]*m[12])); |
2331 |
|
✗ |
r[10] = det *(m[ 0]*(m[ 5]*m[15] - m[ 7]*m[13]) + m[ 1]*(m[ 7]*m[12] - m[ 4]*m[15]) + m[ 3]*(m[ 4]*m[13] - m[ 5]*m[12])); |
2332 |
|
✗ |
r[11] = -det*(m[ 0]*(m[ 5]*m[11] - m[ 7]*m[ 9]) + m[ 1]*(m[ 7]*m[ 8] - m[ 4]*m[11]) + m[ 3]*(m[ 4]*m[ 9] - m[ 5]*m[ 8])); |
2333 |
|
✗ |
r[12] = -det*(m[ 4]*(m[ 9]*m[14] - m[10]*m[13]) + m[ 5]*(m[10]*m[12] - m[ 8]*m[14]) + m[ 6]*(m[ 8]*m[13] - m[ 9]*m[12])); |
2334 |
|
✗ |
r[13] = det *(m[ 0]*(m[ 9]*m[14] - m[10]*m[13]) + m[ 1]*(m[10]*m[12] - m[ 8]*m[14]) + m[ 2]*(m[ 8]*m[13] - m[ 9]*m[12])); |
2335 |
|
✗ |
r[14] = -det*(m[ 0]*(m[ 5]*m[14] - m[ 6]*m[13]) + m[ 1]*(m[ 6]*m[12] - m[ 4]*m[14]) + m[ 2]*(m[ 4]*m[13] - m[ 5]*m[12])); |
2336 |
|
✗ |
r[15] = det *(m[ 0]*(m[ 5]*m[10] - m[ 6]*m[ 9]) + m[ 1]*(m[ 6]*m[ 8] - m[ 4]*m[10]) + m[ 2]*(m[ 4]*m[ 9] - m[ 5]*m[ 8])); |
2337 |
|
|
memcpy(m, &r, sizeof(r)); |
2338 |
|
|
} |
2339 |
|
|
/* compose a coloumn major 4 x 4 matrix from vec3 position and vec4 orientation/rotation quaternion */ |
2340 |
|
✗ |
void _m3d_mat(M3D_FLOAT *r, m3dv_t *p, m3dv_t *q) |
2341 |
|
|
{ |
2342 |
|
✗ |
if(q->x == (M3D_FLOAT)0.0 && q->y == (M3D_FLOAT)0.0 && q->z >=(M3D_FLOAT) 0.7071065 && q->z <= (M3D_FLOAT)0.7071075 && |
2343 |
|
✗ |
q->w == (M3D_FLOAT)0.0) { |
2344 |
|
✗ |
r[ 1] = r[ 2] = r[ 4] = r[ 6] = r[ 8] = r[ 9] = (M3D_FLOAT)0.0; |
2345 |
|
✗ |
r[ 0] = r[ 5] = r[10] = (M3D_FLOAT)-1.0; |
2346 |
|
|
} else { |
2347 |
|
✗ |
r[ 0] = 1 - 2 * (q->y * q->y + q->z * q->z); if(r[ 0]>-M3D_EPSILON && r[ 0]<M3D_EPSILON) r[ 0]=(M3D_FLOAT)0.0; |
2348 |
|
✗ |
r[ 1] = 2 * (q->x * q->y - q->z * q->w); if(r[ 1]>-M3D_EPSILON && r[ 1]<M3D_EPSILON) r[ 1]=(M3D_FLOAT)0.0; |
2349 |
|
✗ |
r[ 2] = 2 * (q->x * q->z + q->y * q->w); if(r[ 2]>-M3D_EPSILON && r[ 2]<M3D_EPSILON) r[ 2]=(M3D_FLOAT)0.0; |
2350 |
|
✗ |
r[ 4] = 2 * (q->x * q->y + q->z * q->w); if(r[ 4]>-M3D_EPSILON && r[ 4]<M3D_EPSILON) r[ 4]=(M3D_FLOAT)0.0; |
2351 |
|
✗ |
r[ 5] = 1 - 2 * (q->x * q->x + q->z * q->z); if(r[ 5]>-M3D_EPSILON && r[ 5]<M3D_EPSILON) r[ 5]=(M3D_FLOAT)0.0; |
2352 |
|
✗ |
r[ 6] = 2 * (q->y * q->z - q->x * q->w); if(r[ 6]>-M3D_EPSILON && r[ 6]<M3D_EPSILON) r[ 6]=(M3D_FLOAT)0.0; |
2353 |
|
✗ |
r[ 8] = 2 * (q->x * q->z - q->y * q->w); if(r[ 8]>-M3D_EPSILON && r[ 8]<M3D_EPSILON) r[ 8]=(M3D_FLOAT)0.0; |
2354 |
|
✗ |
r[ 9] = 2 * (q->y * q->z + q->x * q->w); if(r[ 9]>-M3D_EPSILON && r[ 9]<M3D_EPSILON) r[ 9]=(M3D_FLOAT)0.0; |
2355 |
|
✗ |
r[10] = 1 - 2 * (q->x * q->x + q->y * q->y); if(r[10]>-M3D_EPSILON && r[10]<M3D_EPSILON) r[10]=(M3D_FLOAT)0.0; |
2356 |
|
|
} |
2357 |
|
✗ |
r[ 3] = p->x; r[ 7] = p->y; r[11] = p->z; |
2358 |
|
✗ |
r[12] = 0; r[13] = 0; r[14] = 0; r[15] = 1; |
2359 |
|
|
} |
2360 |
|
|
#endif |
2361 |
|
|
#if !defined(M3D_NOANIMATION) || !defined(M3D_NONORMALS) |
2362 |
|
|
/* portable fast inverse square root calculation. returns 1/sqrt(x) */ |
2363 |
|
|
static M3D_FLOAT _m3d_rsq(M3D_FLOAT x) |
2364 |
|
|
{ |
2365 |
|
|
#ifdef M3D_DOUBLE |
2366 |
|
|
return ((M3D_FLOAT)15.0/(M3D_FLOAT)8.0) + ((M3D_FLOAT)-5.0/(M3D_FLOAT)4.0)*x + ((M3D_FLOAT)3.0/(M3D_FLOAT)8.0)*x*x; |
2367 |
|
|
#else |
2368 |
|
|
/* John Carmack's */ |
2369 |
|
✗ |
float x2 = x * 0.5f; |
2370 |
|
|
uint32_t *i = (uint32_t*)&x; |
2371 |
|
✗ |
*i = (0x5f3759df - (*i >> 1)); |
2372 |
|
✗ |
return x * (1.5f - (x2 * x * x)); |
2373 |
|
|
#endif |
2374 |
|
|
} |
2375 |
|
|
#endif |
2376 |
|
|
|
2377 |
|
|
/** |
2378 |
|
|
* Function to decode a Model 3D into in-memory format |
2379 |
|
|
*/ |
2380 |
|
✗ |
m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d_t *mtllib) |
2381 |
|
|
{ |
2382 |
|
|
unsigned char *end, *chunk, *buff, weights[8]; |
2383 |
|
✗ |
unsigned int i, j, k, l, n, am, len = 0, reclen, offs; |
2384 |
|
|
#ifndef M3D_NOVOXELS |
2385 |
|
|
int32_t min_x, min_y, min_z, max_x, max_y, max_z, sx, sy, sz, x, y, z; |
2386 |
|
|
M3D_INDEX edge[8], enorm; |
2387 |
|
|
#endif |
2388 |
|
|
char *name, *lang; |
2389 |
|
|
float f; |
2390 |
|
|
m3d_t *model; |
2391 |
|
|
M3D_INDEX mi; |
2392 |
|
|
#ifdef M3D_VERTEXMAX |
2393 |
|
|
M3D_INDEX pi; |
2394 |
|
|
#endif |
2395 |
|
|
M3D_FLOAT w; |
2396 |
|
|
m3dcd_t *cd; |
2397 |
|
|
m3dtx_t *tx; |
2398 |
|
|
m3dh_t *h; |
2399 |
|
|
m3dm_t *m; |
2400 |
|
|
m3da_t *a; |
2401 |
|
|
m3di_t *t; |
2402 |
|
|
#ifndef M3D_NONORMALS |
2403 |
|
|
char neednorm = 0; |
2404 |
|
|
m3dv_t *norm = NULL, *v0, *v1, *v2, va, vb; |
2405 |
|
|
#endif |
2406 |
|
|
#ifndef M3D_NOANIMATION |
2407 |
|
|
M3D_FLOAT r[16]; |
2408 |
|
|
#endif |
2409 |
|
|
#if !defined(M3D_NOWEIGHTS) || !defined(M3D_NOANIMATION) |
2410 |
|
|
m3db_t *b; |
2411 |
|
|
#endif |
2412 |
|
|
#ifndef M3D_NOWEIGHTS |
2413 |
|
|
m3ds_t *sk; |
2414 |
|
|
#endif |
2415 |
|
|
#ifdef M3D_ASCII |
2416 |
|
|
m3ds_t s; |
2417 |
|
|
M3D_INDEX bi[M3D_BONEMAXLEVEL+1], level; |
2418 |
|
|
const char *ol; |
2419 |
|
|
char *ptr, *pe, *fn; |
2420 |
|
|
#endif |
2421 |
|
|
#ifdef M3D_PROFILING |
2422 |
|
|
struct timeval tv0, tv1, tvd; |
2423 |
|
|
gettimeofday(&tv0, NULL); |
2424 |
|
|
#endif |
2425 |
|
|
|
2426 |
|
✗ |
if(!data || (!M3D_CHUNKMAGIC(data, '3','D','M','O') |
2427 |
|
|
#ifdef M3D_ASCII |
2428 |
|
|
&& !M3D_CHUNKMAGIC(data, '3','d','m','o') |
2429 |
|
|
#endif |
2430 |
|
|
)) return NULL; |
2431 |
|
✗ |
model = (m3d_t*)M3D_MALLOC(sizeof(m3d_t)); |
2432 |
|
✗ |
if(!model) { |
2433 |
|
|
M3D_LOG("Out of memory"); |
2434 |
|
|
return NULL; |
2435 |
|
|
} |
2436 |
|
|
memset(model, 0, sizeof(m3d_t)); |
2437 |
|
|
|
2438 |
|
✗ |
if(mtllib) { |
2439 |
|
✗ |
model->nummaterial = mtllib->nummaterial; |
2440 |
|
✗ |
model->material = mtllib->material; |
2441 |
|
✗ |
model->numtexture = mtllib->numtexture; |
2442 |
|
✗ |
model->texture = mtllib->texture; |
2443 |
|
✗ |
model->flags |= M3D_FLG_MTLLIB; |
2444 |
|
|
} |
2445 |
|
|
#ifdef M3D_ASCII |
2446 |
|
|
/* ASCII variant? */ |
2447 |
|
|
if(M3D_CHUNKMAGIC(data, '3','d','m','o')) { |
2448 |
|
|
model->errcode = M3D_ERR_BADFILE; |
2449 |
|
|
model->flags |= M3D_FLG_FREESTR; |
2450 |
|
|
model->raw = (m3dhdr_t*)data; |
2451 |
|
|
ptr = (char*)data; |
2452 |
|
|
ol = setlocale(LC_NUMERIC, NULL); |
2453 |
|
|
setlocale(LC_NUMERIC, "C"); |
2454 |
|
|
/* parse header. Don't use sscanf, that's incredibly slow */ |
2455 |
|
|
ptr = _m3d_findarg(ptr); |
2456 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2457 |
|
|
pe = _m3d_findnl(ptr); |
2458 |
|
|
model->scale = (float)strtod(ptr, NULL); ptr = pe; |
2459 |
|
|
if(model->scale <= (M3D_FLOAT)0.0) model->scale = (M3D_FLOAT)1.0; |
2460 |
|
|
model->name = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr); |
2461 |
|
|
if(!*ptr) goto asciiend; |
2462 |
|
|
model->license = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr); |
2463 |
|
|
if(!*ptr) goto asciiend; |
2464 |
|
|
model->author = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr); |
2465 |
|
|
if(!*ptr) goto asciiend; |
2466 |
|
|
if(*ptr != '\r' && *ptr != '\n') |
2467 |
|
|
model->desc = _m3d_safestr(ptr, 3); |
2468 |
|
|
while(*ptr) { |
2469 |
|
|
while(*ptr && *ptr!='\n') ptr++; |
2470 |
|
|
ptr++; if(*ptr=='\r') ptr++; |
2471 |
|
|
if(*ptr == '\n') break; |
2472 |
|
|
} |
2473 |
|
|
|
2474 |
|
|
/* the main chunk reader loop */ |
2475 |
|
|
while(*ptr) { |
2476 |
|
|
while(*ptr && (*ptr == '\r' || *ptr == '\n')) ptr++; |
2477 |
|
|
if(!*ptr || (ptr[0]=='E' && ptr[1]=='n' && ptr[2]=='d')) break; |
2478 |
|
|
/* make sure there's at least one data row */ |
2479 |
|
|
pe = ptr; ptr = _m3d_findnl(ptr); |
2480 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2481 |
|
|
/* Preview chunk */ |
2482 |
|
|
if(!memcmp(pe, "Preview", 7)) { |
2483 |
|
|
if(readfilecb) { |
2484 |
|
|
pe = _m3d_safestr(ptr, 0); |
2485 |
|
|
if(!pe || !*pe) goto asciiend; |
2486 |
|
|
model->preview.data = (*readfilecb)(pe, &model->preview.length); |
2487 |
|
|
M3D_FREE(pe); |
2488 |
|
|
} |
2489 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') |
2490 |
|
|
ptr = _m3d_findnl(ptr); |
2491 |
|
|
} else |
2492 |
|
|
/* texture map chunk */ |
2493 |
|
|
if(!memcmp(pe, "Textmap", 7)) { |
2494 |
|
|
if(model->tmap) { M3D_LOG("More texture map chunks, should be unique"); goto asciiend; } |
2495 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
2496 |
|
|
i = model->numtmap++; |
2497 |
|
|
model->tmap = (m3dti_t*)M3D_REALLOC(model->tmap, model->numtmap * sizeof(m3dti_t)); |
2498 |
|
|
if(!model->tmap) goto memerr; |
2499 |
|
|
ptr = _m3d_getfloat(ptr, &model->tmap[i].u); |
2500 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2501 |
|
|
_m3d_getfloat(ptr, &model->tmap[i].v); |
2502 |
|
|
ptr = _m3d_findnl(ptr); |
2503 |
|
|
} |
2504 |
|
|
} else |
2505 |
|
|
/* vertex chunk */ |
2506 |
|
|
if(!memcmp(pe, "Vertex", 6)) { |
2507 |
|
|
if(model->vertex) { M3D_LOG("More vertex chunks, should be unique"); goto asciiend; } |
2508 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
2509 |
|
|
i = model->numvertex++; |
2510 |
|
|
model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t)); |
2511 |
|
|
if(!model->vertex) goto memerr; |
2512 |
|
|
memset(&model->vertex[i], 0, sizeof(m3dv_t)); |
2513 |
|
|
model->vertex[i].skinid = M3D_UNDEF; |
2514 |
|
|
model->vertex[i].color = 0; |
2515 |
|
|
model->vertex[i].w = (M3D_FLOAT)1.0; |
2516 |
|
|
ptr = _m3d_getfloat(ptr, &model->vertex[i].x); |
2517 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2518 |
|
|
ptr = _m3d_getfloat(ptr, &model->vertex[i].y); |
2519 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2520 |
|
|
ptr = _m3d_getfloat(ptr, &model->vertex[i].z); |
2521 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2522 |
|
|
ptr = _m3d_getfloat(ptr, &model->vertex[i].w); |
2523 |
|
|
if(!*ptr) goto asciiend; |
2524 |
|
|
if(*ptr == '#') { |
2525 |
|
|
ptr = _m3d_gethex(ptr, &model->vertex[i].color); |
2526 |
|
|
if(!*ptr) goto asciiend; |
2527 |
|
|
} |
2528 |
|
|
/* parse skin */ |
2529 |
|
|
memset(&s, 0, sizeof(m3ds_t)); |
2530 |
|
|
for(j = 0, w = (M3D_FLOAT)0.0; j < M3D_NUMBONE && *ptr && *ptr != '\r' && *ptr != '\n'; j++) { |
2531 |
|
|
ptr = _m3d_findarg(ptr); |
2532 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2533 |
|
|
ptr = _m3d_getint(ptr, &k); |
2534 |
|
|
s.boneid[j] = (M3D_INDEX)k; |
2535 |
|
|
if(*ptr == ':') { |
2536 |
|
|
ptr++; |
2537 |
|
|
ptr = _m3d_getfloat(ptr, &s.weight[j]); |
2538 |
|
|
w += s.weight[j]; |
2539 |
|
|
} else if(!j) |
2540 |
|
|
s.weight[j] = (M3D_FLOAT)1.0; |
2541 |
|
|
if(!*ptr) goto asciiend; |
2542 |
|
|
} |
2543 |
|
|
if(s.boneid[0] != M3D_UNDEF && s.weight[0] > (M3D_FLOAT)0.0) { |
2544 |
|
|
if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0) |
2545 |
|
|
for(j = 0; j < M3D_NUMBONE && s.weight[j] > (M3D_FLOAT)0.0; j++) |
2546 |
|
|
s.weight[j] /= w; |
2547 |
|
|
k = M3D_NOTDEFINED; |
2548 |
|
|
if(model->skin) { |
2549 |
|
|
for(j = 0; j < model->numskin; j++) |
2550 |
|
|
if(!memcmp(&model->skin[j], &s, sizeof(m3ds_t))) { k = j; break; } |
2551 |
|
|
} |
2552 |
|
|
if(k == M3D_NOTDEFINED) { |
2553 |
|
|
k = model->numskin++; |
2554 |
|
|
model->skin = (m3ds_t*)M3D_REALLOC(model->skin, model->numskin * sizeof(m3ds_t)); |
2555 |
|
|
if(!model->skin) goto memerr; |
2556 |
|
|
memcpy(&model->skin[k], &s, sizeof(m3ds_t)); |
2557 |
|
|
} |
2558 |
|
|
model->vertex[i].skinid = (M3D_INDEX)k; |
2559 |
|
|
} |
2560 |
|
|
ptr = _m3d_findnl(ptr); |
2561 |
|
|
} |
2562 |
|
|
} else |
2563 |
|
|
/* Skeleton, bone hierarchy */ |
2564 |
|
|
if(!memcmp(pe, "Bones", 5)) { |
2565 |
|
|
if(model->bone) { M3D_LOG("More bones chunks, should be unique"); goto asciiend; } |
2566 |
|
|
bi[0] = M3D_UNDEF; |
2567 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
2568 |
|
|
i = model->numbone++; |
2569 |
|
|
model->bone = (m3db_t*)M3D_REALLOC(model->bone, model->numbone * sizeof(m3db_t)); |
2570 |
|
|
if(!model->bone) goto memerr; |
2571 |
|
|
for(level = 0; *ptr == '/'; ptr++, level++); |
2572 |
|
|
if(level > M3D_BONEMAXLEVEL || !*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2573 |
|
|
bi[level+1] = i; |
2574 |
|
|
model->bone[i].numweight = 0; |
2575 |
|
|
model->bone[i].weight = NULL; |
2576 |
|
|
model->bone[i].parent = bi[level]; |
2577 |
|
|
ptr = _m3d_getint(ptr, &k); |
2578 |
|
|
ptr = _m3d_findarg(ptr); |
2579 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2580 |
|
|
model->bone[i].pos = (M3D_INDEX)k; |
2581 |
|
|
ptr = _m3d_getint(ptr, &k); |
2582 |
|
|
ptr = _m3d_findarg(ptr); |
2583 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2584 |
|
|
model->bone[i].ori = (M3D_INDEX)k; |
2585 |
|
|
model->vertex[k].skinid = M3D_INDEXMAX; |
2586 |
|
|
pe = _m3d_safestr(ptr, 0); |
2587 |
|
|
if(!pe || !*pe) goto asciiend; |
2588 |
|
|
model->bone[i].name = pe; |
2589 |
|
|
ptr = _m3d_findnl(ptr); |
2590 |
|
|
} |
2591 |
|
|
} else |
2592 |
|
|
/* material chunk */ |
2593 |
|
|
if(!memcmp(pe, "Material", 8)) { |
2594 |
|
|
pe = _m3d_findarg(pe); |
2595 |
|
|
if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; |
2596 |
|
|
pe = _m3d_safestr(pe, 0); |
2597 |
|
|
if(!pe || !*pe) goto asciiend; |
2598 |
|
|
for(i = 0; i < model->nummaterial; i++) |
2599 |
|
|
if(!strcmp(pe, model->material[i].name)) { |
2600 |
|
|
M3D_LOG("Multiple definitions for material"); |
2601 |
|
|
M3D_LOG(pe); |
2602 |
|
|
M3D_FREE(pe); |
2603 |
|
|
pe = NULL; |
2604 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') ptr = _m3d_findnl(ptr); |
2605 |
|
|
break; |
2606 |
|
|
} |
2607 |
|
|
if(!pe) continue; |
2608 |
|
|
i = model->nummaterial++; |
2609 |
|
|
if(model->flags & M3D_FLG_MTLLIB) { |
2610 |
|
|
m = model->material; |
2611 |
|
|
model->material = (m3dm_t*)M3D_MALLOC(model->nummaterial * sizeof(m3dm_t)); |
2612 |
|
|
if(!model->material) goto memerr; |
2613 |
|
|
memcpy(model->material, m, (model->nummaterial - 1) * sizeof(m3dm_t)); |
2614 |
|
|
if(model->texture) { |
2615 |
|
|
tx = model->texture; |
2616 |
|
|
model->texture = (m3dtx_t*)M3D_MALLOC(model->numtexture * sizeof(m3dtx_t)); |
2617 |
|
|
if(!model->texture) goto memerr; |
2618 |
|
|
memcpy(model->texture, tx, model->numtexture * sizeof(m3dm_t)); |
2619 |
|
|
} |
2620 |
|
|
model->flags &= ~M3D_FLG_MTLLIB; |
2621 |
|
|
} else { |
2622 |
|
|
model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t)); |
2623 |
|
|
if(!model->material) goto memerr; |
2624 |
|
|
} |
2625 |
|
|
m = &model->material[i]; |
2626 |
|
|
m->name = pe; |
2627 |
|
|
m->numprop = 0; |
2628 |
|
|
m->prop = NULL; |
2629 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
2630 |
|
|
k = n = 256; |
2631 |
|
|
if(*ptr == 'm' && *(ptr+1) == 'a' && *(ptr+2) == 'p' && *(ptr+3) == '_') { |
2632 |
|
|
k = m3dpf_map; |
2633 |
|
|
ptr += 4; |
2634 |
|
|
} |
2635 |
|
|
for(j = 0; j < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); j++) |
2636 |
|
|
if(!memcmp(ptr, m3d_propertytypes[j].key, strlen(m3d_propertytypes[j].key))) { |
2637 |
|
|
n = m3d_propertytypes[j].id; |
2638 |
|
|
if(k != m3dpf_map) k = m3d_propertytypes[j].format; |
2639 |
|
|
break; |
2640 |
|
|
} |
2641 |
|
|
if(n != 256 && k != 256) { |
2642 |
|
|
ptr = _m3d_findarg(ptr); |
2643 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2644 |
|
|
j = m->numprop++; |
2645 |
|
|
m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t)); |
2646 |
|
|
if(!m->prop) goto memerr; |
2647 |
|
|
m->prop[j].type = n + (k == m3dpf_map && n < 128 ? 128 : 0); |
2648 |
|
|
switch(k) { |
2649 |
|
|
case m3dpf_color: ptr = _m3d_gethex(ptr, &m->prop[j].value.color); break; |
2650 |
|
|
case m3dpf_uint8: |
2651 |
|
|
case m3dpf_uint16: |
2652 |
|
|
case m3dpf_uint32: ptr = _m3d_getint(ptr, &m->prop[j].value.num); break; |
2653 |
|
|
case m3dpf_float: ptr = _m3d_getfloat(ptr, &m->prop[j].value.fnum); break; |
2654 |
|
|
case m3dpf_map: |
2655 |
|
|
pe = _m3d_safestr(ptr, 0); |
2656 |
|
|
if(!pe || !*pe) goto asciiend; |
2657 |
|
|
m->prop[j].value.textureid = _m3d_gettx(model, readfilecb, freecb, pe); |
2658 |
|
|
if(model->errcode == M3D_ERR_ALLOC) { M3D_FREE(pe); goto memerr; } |
2659 |
|
|
/* this error code only returned if readfilecb was specified */ |
2660 |
|
|
if(m->prop[j].value.textureid == M3D_UNDEF) { |
2661 |
|
|
M3D_LOG("Texture not found"); |
2662 |
|
|
M3D_LOG(pe); |
2663 |
|
|
m->numprop--; |
2664 |
|
|
} |
2665 |
|
|
M3D_FREE(pe); |
2666 |
|
|
break; |
2667 |
|
|
} |
2668 |
|
|
} else { |
2669 |
|
|
M3D_LOG("Unknown material property in"); |
2670 |
|
|
M3D_LOG(m->name); |
2671 |
|
|
model->errcode = M3D_ERR_UNKPROP; |
2672 |
|
|
} |
2673 |
|
|
ptr = _m3d_findnl(ptr); |
2674 |
|
|
} |
2675 |
|
|
if(!m->numprop) model->nummaterial--; |
2676 |
|
|
} else |
2677 |
|
|
/* procedural */ |
2678 |
|
|
if(!memcmp(pe, "Procedural", 10)) { |
2679 |
|
|
pe = _m3d_safestr(ptr, 0); |
2680 |
|
|
_m3d_getpr(model, readfilecb, freecb, pe); |
2681 |
|
|
M3D_FREE(pe); |
2682 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') ptr = _m3d_findnl(ptr); |
2683 |
|
|
} else |
2684 |
|
|
/* mesh */ |
2685 |
|
|
if(!memcmp(pe, "Mesh", 4)) { |
2686 |
|
|
mi = M3D_UNDEF; |
2687 |
|
|
#ifdef M3D_VERTEXMAX |
2688 |
|
|
pi = M3D_UNDEF; |
2689 |
|
|
#endif |
2690 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
2691 |
|
|
if(*ptr == 'u') { |
2692 |
|
|
ptr = _m3d_findarg(ptr); |
2693 |
|
|
if(!*ptr) goto asciiend; |
2694 |
|
|
mi = M3D_UNDEF; |
2695 |
|
|
if(*ptr != '\r' && *ptr != '\n') { |
2696 |
|
|
pe = _m3d_safestr(ptr, 0); |
2697 |
|
|
if(!pe || !*pe) goto asciiend; |
2698 |
|
|
for(j = 0; j < model->nummaterial; j++) |
2699 |
|
|
if(!strcmp(pe, model->material[j].name)) { mi = (M3D_INDEX)j; break; } |
2700 |
|
|
if(mi == M3D_UNDEF && !(model->flags & M3D_FLG_MTLLIB)) { |
2701 |
|
|
mi = model->nummaterial++; |
2702 |
|
|
model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t)); |
2703 |
|
|
if(!model->material) goto memerr; |
2704 |
|
|
model->material[mi].name = pe; |
2705 |
|
|
model->material[mi].numprop = 1; |
2706 |
|
|
model->material[mi].prop = NULL; |
2707 |
|
|
} else |
2708 |
|
|
M3D_FREE(pe); |
2709 |
|
|
} |
2710 |
|
|
} else |
2711 |
|
|
if(*ptr == 'p') { |
2712 |
|
|
ptr = _m3d_findarg(ptr); |
2713 |
|
|
if(!*ptr) goto asciiend; |
2714 |
|
|
#ifdef M3D_VERTEXMAX |
2715 |
|
|
pi = M3D_UNDEF; |
2716 |
|
|
if(*ptr != '\r' && *ptr != '\n') { |
2717 |
|
|
pe = _m3d_safestr(ptr, 0); |
2718 |
|
|
if(!pe || !*pe) goto asciiend; |
2719 |
|
|
for(j = 0; j < model->numparam; j++) |
2720 |
|
|
if(!strcmp(pe, model->param[j].name)) { pi = (M3D_INDEX)j; break; } |
2721 |
|
|
if(pi == M3D_UNDEF) { |
2722 |
|
|
pi = model->numparam++; |
2723 |
|
|
model->param = (m3dvi_t*)M3D_REALLOC(model->param, model->numparam * sizeof(m3dvi_t)); |
2724 |
|
|
if(!model->param) goto memerr; |
2725 |
|
|
model->param[pi].name = pe; |
2726 |
|
|
model->param[pi].count = 0; |
2727 |
|
|
} else |
2728 |
|
|
M3D_FREE(pe); |
2729 |
|
|
} |
2730 |
|
|
#endif |
2731 |
|
|
} else { |
2732 |
|
|
i = model->numface++; |
2733 |
|
|
model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t)); |
2734 |
|
|
if(!model->face) goto memerr; |
2735 |
|
|
memset(&model->face[i], 255, sizeof(m3df_t)); /* set all index to -1 by default */ |
2736 |
|
|
model->face[i].materialid = mi; |
2737 |
|
|
#ifdef M3D_VERTEXMAX |
2738 |
|
|
model->face[i].paramid = pi; |
2739 |
|
|
#endif |
2740 |
|
|
/* hardcoded triangles. */ |
2741 |
|
|
for(j = 0; j < 3; j++) { |
2742 |
|
|
/* vertex */ |
2743 |
|
|
ptr = _m3d_getint(ptr, &k); |
2744 |
|
|
model->face[i].vertex[j] = (M3D_INDEX)k; |
2745 |
|
|
if(!*ptr) goto asciiend; |
2746 |
|
|
if(*ptr == '/') { |
2747 |
|
|
ptr++; |
2748 |
|
|
if(*ptr != '/') { |
2749 |
|
|
/* texcoord */ |
2750 |
|
|
ptr = _m3d_getint(ptr, &k); |
2751 |
|
|
model->face[i].texcoord[j] = (M3D_INDEX)k; |
2752 |
|
|
if(!*ptr) goto asciiend; |
2753 |
|
|
} |
2754 |
|
|
if(*ptr == '/') { |
2755 |
|
|
ptr++; |
2756 |
|
|
/* normal */ |
2757 |
|
|
ptr = _m3d_getint(ptr, &k); |
2758 |
|
|
model->face[i].normal[j] = (M3D_INDEX)k; |
2759 |
|
|
if(!*ptr) goto asciiend; |
2760 |
|
|
} |
2761 |
|
|
if(*ptr == '/') { |
2762 |
|
|
ptr++; |
2763 |
|
|
/* maximum */ |
2764 |
|
|
ptr = _m3d_getint(ptr, &k); |
2765 |
|
|
#ifdef M3D_VERTEXMAX |
2766 |
|
|
model->face[i].vertmax[j] = (M3D_INDEX)k; |
2767 |
|
|
#endif |
2768 |
|
|
if(!*ptr) goto asciiend; |
2769 |
|
|
} |
2770 |
|
|
} |
2771 |
|
|
#ifndef M3D_NONORMALS |
2772 |
|
|
if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1; |
2773 |
|
|
#endif |
2774 |
|
|
ptr = _m3d_findarg(ptr); |
2775 |
|
|
} |
2776 |
|
|
} |
2777 |
|
|
ptr = _m3d_findnl(ptr); |
2778 |
|
|
} |
2779 |
|
|
} else |
2780 |
|
|
/* voxel types chunk */ |
2781 |
|
|
if(!memcmp(pe, "VoxTypes", 8) || !memcmp(pe, "Voxtypes", 8)) { |
2782 |
|
|
if(model->voxtype) { M3D_LOG("More voxel types chunks, should be unique"); goto asciiend; } |
2783 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
2784 |
|
|
i = model->numvoxtype++; |
2785 |
|
|
model->voxtype = (m3dvt_t*)M3D_REALLOC(model->voxtype, model->numvoxtype * sizeof(m3dvt_t)); |
2786 |
|
|
if(!model->voxtype) goto memerr; |
2787 |
|
|
memset(&model->voxtype[i], 0, sizeof(m3dvt_t)); |
2788 |
|
|
model->voxtype[i].materialid = M3D_UNDEF; |
2789 |
|
|
model->voxtype[i].skinid = M3D_UNDEF; |
2790 |
|
|
ptr = _m3d_gethex(ptr, &model->voxtype[i].color); |
2791 |
|
|
if(!*ptr) goto asciiend; |
2792 |
|
|
if(*ptr == '/') { |
2793 |
|
|
ptr = _m3d_gethex(ptr, &k); |
2794 |
|
|
model->voxtype[i].rotation = k; |
2795 |
|
|
if(!*ptr) goto asciiend; |
2796 |
|
|
if(*ptr == '/') { |
2797 |
|
|
ptr = _m3d_gethex(ptr, &k); |
2798 |
|
|
model->voxtype[i].voxshape = k; |
2799 |
|
|
if(!*ptr) goto asciiend; |
2800 |
|
|
} |
2801 |
|
|
} |
2802 |
|
|
while(*ptr == ' ' || *ptr == '\t') ptr++; |
2803 |
|
|
if(*ptr == '\r' || *ptr == '\n') { ptr = _m3d_findnl(ptr); continue; } |
2804 |
|
|
/* name */ |
2805 |
|
|
if(*ptr != '-') { |
2806 |
|
|
pe = _m3d_safestr(ptr, 0); |
2807 |
|
|
if(!pe || !*pe) goto asciiend; |
2808 |
|
|
model->voxtype[i].name = pe; |
2809 |
|
|
for(j = 0; j < model->nummaterial; j++) |
2810 |
|
|
if(!strcmp(pe, model->material[j].name)) { model->voxtype[i].materialid = (M3D_INDEX)j; break; } |
2811 |
|
|
} |
2812 |
|
|
ptr = _m3d_findarg(ptr); |
2813 |
|
|
/* parse skin */ |
2814 |
|
|
memset(&s, 0, sizeof(m3ds_t)); |
2815 |
|
|
for(j = 0, w = (M3D_FLOAT)0.0; j < M3D_NUMBONE && *ptr && *ptr != '{' && *ptr != '\r' && *ptr != '\n'; j++) { |
2816 |
|
|
ptr = _m3d_getint(ptr, &k); |
2817 |
|
|
s.boneid[j] = (M3D_INDEX)k; |
2818 |
|
|
if(*ptr == ':') { |
2819 |
|
|
ptr++; |
2820 |
|
|
ptr = _m3d_getfloat(ptr, &s.weight[j]); |
2821 |
|
|
w += s.weight[j]; |
2822 |
|
|
} else if(!j) |
2823 |
|
|
s.weight[j] = (M3D_FLOAT)1.0; |
2824 |
|
|
if(!*ptr) goto asciiend; |
2825 |
|
|
ptr = _m3d_findarg(ptr); |
2826 |
|
|
} |
2827 |
|
|
if(s.boneid[0] != M3D_UNDEF && s.weight[0] > (M3D_FLOAT)0.0) { |
2828 |
|
|
if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0) |
2829 |
|
|
for(j = 0; j < M3D_NUMBONE && s.weight[j] > (M3D_FLOAT)0.0; j++) |
2830 |
|
|
s.weight[j] /= w; |
2831 |
|
|
k = M3D_NOTDEFINED; |
2832 |
|
|
if(model->skin) { |
2833 |
|
|
for(j = 0; j < model->numskin; j++) |
2834 |
|
|
if(!memcmp(&model->skin[j], &s, sizeof(m3ds_t))) { k = j; break; } |
2835 |
|
|
} |
2836 |
|
|
if(k == M3D_NOTDEFINED) { |
2837 |
|
|
k = model->numskin++; |
2838 |
|
|
model->skin = (m3ds_t*)M3D_REALLOC(model->skin, model->numskin * sizeof(m3ds_t)); |
2839 |
|
|
if(!model->skin) goto memerr; |
2840 |
|
|
memcpy(&model->skin[k], &s, sizeof(m3ds_t)); |
2841 |
|
|
} |
2842 |
|
|
model->voxtype[i].skinid = (M3D_INDEX)k; |
2843 |
|
|
} |
2844 |
|
|
/* parse item list */ |
2845 |
|
|
if(*ptr == '{') { |
2846 |
|
|
while(*ptr == '{' || *ptr == ' ' || *ptr == '\t') ptr++; |
2847 |
|
|
while(*ptr && *ptr != '}' && *ptr != '\r' && *ptr != '\n') { |
2848 |
|
|
ptr = _m3d_getint(ptr, &k); |
2849 |
|
|
ptr = _m3d_findarg(ptr); |
2850 |
|
|
if(!*ptr || *ptr == '}' || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2851 |
|
|
pe = _m3d_safestr(ptr, 0); |
2852 |
|
|
if(!pe || !*pe) goto asciiend; |
2853 |
|
|
ptr = _m3d_findarg(ptr); |
2854 |
|
|
j = model->voxtype[i].numitem++; |
2855 |
|
|
model->voxtype[i].item = (m3dvi_t*)M3D_REALLOC(model->voxtype[i].item, |
2856 |
|
|
model->voxtype[i].numitem * sizeof(m3dvi_t)); |
2857 |
|
|
if(!model->voxtype[i].item) goto memerr; |
2858 |
|
|
model->voxtype[i].item[j].count = k; |
2859 |
|
|
model->voxtype[i].item[j].name = pe; |
2860 |
|
|
} |
2861 |
|
|
if(*ptr != '}') goto asciiend; |
2862 |
|
|
} |
2863 |
|
|
ptr = _m3d_findnl(ptr); |
2864 |
|
|
} |
2865 |
|
|
} else |
2866 |
|
|
/* voxel data */ |
2867 |
|
|
if(!memcmp(pe, "Voxel", 5)) { |
2868 |
|
|
if(!model->voxtype) { M3D_LOG("No voxel type chunk before voxel data"); goto asciiend; } |
2869 |
|
|
pe = _m3d_findarg(pe); |
2870 |
|
|
if(!*pe) goto asciiend; |
2871 |
|
|
if(*pe == '\r' || *pe == '\n') pe = NULL; |
2872 |
|
|
else pe = _m3d_safestr(pe, 0); |
2873 |
|
|
i = model->numvoxel++; |
2874 |
|
|
model->voxel = (m3dvx_t*)M3D_REALLOC(model->voxel, model->numvoxel * sizeof(m3dvx_t)); |
2875 |
|
|
if(!model->voxel) goto memerr; |
2876 |
|
|
memset(&model->voxel[i], 0, sizeof(m3dvx_t)); |
2877 |
|
|
model->voxel[i].name = pe; |
2878 |
|
|
k = l = 0; |
2879 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
2880 |
|
|
switch(*ptr) { |
2881 |
|
|
case 'u': |
2882 |
|
|
ptr = _m3d_findarg(ptr); |
2883 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2884 |
|
|
ptr = _m3d_getint(ptr, &n); |
2885 |
|
|
model->voxel[i].uncertain = ((n > 0 && n < 256 ? n : 0) * 255) / 100; |
2886 |
|
|
ptr = _m3d_findarg(ptr); |
2887 |
|
|
if(*ptr && *ptr != '\r' && *ptr != '\n') { |
2888 |
|
|
ptr = _m3d_getint(ptr, &n); |
2889 |
|
|
model->voxel[i].groupid = n > 0 && n < 256 ? n : 0; |
2890 |
|
|
} |
2891 |
|
|
break; |
2892 |
|
|
case 'p': |
2893 |
|
|
ptr = _m3d_findarg(ptr); |
2894 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2895 |
|
|
ptr = _m3d_getint(ptr, &n); |
2896 |
|
|
model->voxel[i].x = n; |
2897 |
|
|
ptr = _m3d_findarg(ptr); |
2898 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2899 |
|
|
ptr = _m3d_getint(ptr, &n); |
2900 |
|
|
model->voxel[i].y = n; |
2901 |
|
|
ptr = _m3d_findarg(ptr); |
2902 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2903 |
|
|
ptr = _m3d_getint(ptr, &n); |
2904 |
|
|
model->voxel[i].z = n; |
2905 |
|
|
break; |
2906 |
|
|
case 'd': |
2907 |
|
|
ptr = _m3d_findarg(ptr); |
2908 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2909 |
|
|
ptr = _m3d_getint(ptr, &n); |
2910 |
|
|
model->voxel[i].w = n; |
2911 |
|
|
ptr = _m3d_findarg(ptr); |
2912 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2913 |
|
|
ptr = _m3d_getint(ptr, &n); |
2914 |
|
|
model->voxel[i].h = n; |
2915 |
|
|
ptr = _m3d_findarg(ptr); |
2916 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
2917 |
|
|
ptr = _m3d_getint(ptr, &n); |
2918 |
|
|
model->voxel[i].d = n; |
2919 |
|
|
break; |
2920 |
|
|
case 'l': |
2921 |
|
|
if(model->voxel[i].data) { l++; k = 0; } |
2922 |
|
|
else { |
2923 |
|
|
if(!model->voxel[i].w || !model->voxel[i].h || !model->voxel[i].d) { |
2924 |
|
|
M3D_LOG("No voxel dimension before layer data"); |
2925 |
|
|
goto asciiend; |
2926 |
|
|
} |
2927 |
|
|
model->voxel[i].data = (M3D_VOXEL*)M3D_MALLOC( |
2928 |
|
|
model->voxel[i].w * model->voxel[i].h * model->voxel[i].d * sizeof(M3D_VOXEL)); |
2929 |
|
|
if(!model->voxel[i].data) goto memerr; |
2930 |
|
|
} |
2931 |
|
|
break; |
2932 |
|
|
default: |
2933 |
|
|
if(!model->voxel[i].data || l >= model->voxel[i].h || k >= model->voxel[i].d) { |
2934 |
|
|
M3D_LOG("Missing voxel attributes or out of bound data"); |
2935 |
|
|
goto asciiend; |
2936 |
|
|
} |
2937 |
|
|
for(n = l * model->voxel[i].w * model->voxel[i].d + k * model->voxel[i].w; |
2938 |
|
|
j < model->voxel[i].w && *ptr && *ptr != '\r' && *ptr != '\n'; j++) { |
2939 |
|
|
ptr = _m3d_getint(ptr, &am); |
2940 |
|
|
if(am >= model->numvoxtype) goto asciiend; |
2941 |
|
|
model->voxel[i].data[n + j] = am; |
2942 |
|
|
} |
2943 |
|
|
k++; |
2944 |
|
|
break; |
2945 |
|
|
} |
2946 |
|
|
ptr = _m3d_findnl(ptr); |
2947 |
|
|
} |
2948 |
|
|
} else |
2949 |
|
|
/* mathematical shape */ |
2950 |
|
|
if(!memcmp(pe, "Shape", 5)) { |
2951 |
|
|
pe = _m3d_findarg(pe); |
2952 |
|
|
if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; |
2953 |
|
|
pe = _m3d_safestr(pe, 0); |
2954 |
|
|
if(!pe || !*pe) goto asciiend; |
2955 |
|
|
i = model->numshape++; |
2956 |
|
|
model->shape = (m3dh_t*)M3D_REALLOC(model->shape, model->numshape * sizeof(m3ds_t)); |
2957 |
|
|
if(!model->shape) goto memerr; |
2958 |
|
|
h = &model->shape[i]; |
2959 |
|
|
h->name = pe; |
2960 |
|
|
h->group = M3D_UNDEF; |
2961 |
|
|
h->numcmd = 0; |
2962 |
|
|
h->cmd = NULL; |
2963 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
2964 |
|
|
if(!memcmp(ptr, "group", 5)) { |
2965 |
|
|
ptr = _m3d_findarg(ptr); |
2966 |
|
|
ptr = _m3d_getint(ptr, &h->group); |
2967 |
|
|
ptr = _m3d_findnl(ptr); |
2968 |
|
|
if(h->group != M3D_UNDEF && h->group >= model->numbone) { |
2969 |
|
|
M3D_LOG("Unknown bone id as shape group in shape"); |
2970 |
|
|
M3D_LOG(pe); |
2971 |
|
|
h->group = M3D_UNDEF; |
2972 |
|
|
model->errcode = M3D_ERR_SHPE; |
2973 |
|
|
} |
2974 |
|
|
continue; |
2975 |
|
|
} |
2976 |
|
|
for(cd = NULL, k = 0; k < (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])); k++) { |
2977 |
|
|
j = (unsigned int)strlen(m3d_commandtypes[k].key); |
2978 |
|
|
if(!memcmp(ptr, m3d_commandtypes[k].key, j) && (ptr[j] == ' ' || ptr[j] == '\r' || ptr[j] == '\n')) |
2979 |
|
|
{ cd = &m3d_commandtypes[k]; break; } |
2980 |
|
|
} |
2981 |
|
|
if(cd) { |
2982 |
|
|
j = h->numcmd++; |
2983 |
|
|
h->cmd = (m3dc_t*)M3D_REALLOC(h->cmd, h->numcmd * sizeof(m3dc_t)); |
2984 |
|
|
if(!h->cmd) goto memerr; |
2985 |
|
|
h->cmd[j].type = k; |
2986 |
|
|
h->cmd[j].arg = (uint32_t*)M3D_MALLOC(cd->p * sizeof(uint32_t)); |
2987 |
|
|
if(!h->cmd[j].arg) goto memerr; |
2988 |
|
|
memset(h->cmd[j].arg, 0, cd->p * sizeof(uint32_t)); |
2989 |
|
|
for(k = n = 0, l = cd->p; k < l; k++) { |
2990 |
|
|
ptr = _m3d_findarg(ptr); |
2991 |
|
|
if(!*ptr) goto asciiend; |
2992 |
|
|
if(*ptr == '[') { |
2993 |
|
|
ptr = _m3d_findarg(ptr + 1); |
2994 |
|
|
if(!*ptr) goto asciiend; |
2995 |
|
|
} |
2996 |
|
|
if(*ptr == ']' || *ptr == '\r' || *ptr == '\n') break; |
2997 |
|
|
switch(cd->a[((k - n) % (cd->p - n)) + n]) { |
2998 |
|
|
case m3dcp_mi_t: |
2999 |
|
|
mi = M3D_UNDEF; |
3000 |
|
|
if(*ptr != '\r' && *ptr != '\n') { |
3001 |
|
|
pe = _m3d_safestr(ptr, 0); |
3002 |
|
|
if(!pe || !*pe) goto asciiend; |
3003 |
|
|
for(n = 0; n < model->nummaterial; n++) |
3004 |
|
|
if(!strcmp(pe, model->material[n].name)) { mi = (M3D_INDEX)n; break; } |
3005 |
|
|
if(mi == M3D_UNDEF && !(model->flags & M3D_FLG_MTLLIB)) { |
3006 |
|
|
mi = model->nummaterial++; |
3007 |
|
|
model->material = (m3dm_t*)M3D_REALLOC(model->material, |
3008 |
|
|
model->nummaterial * sizeof(m3dm_t)); |
3009 |
|
|
if(!model->material) goto memerr; |
3010 |
|
|
model->material[mi].name = pe; |
3011 |
|
|
model->material[mi].numprop = 1; |
3012 |
|
|
model->material[mi].prop = NULL; |
3013 |
|
|
} else |
3014 |
|
|
M3D_FREE(pe); |
3015 |
|
|
} |
3016 |
|
|
h->cmd[j].arg[k] = mi; |
3017 |
|
|
break; |
3018 |
|
|
case m3dcp_vc_t: |
3019 |
|
|
#ifdef M3D_DOUBLE |
3020 |
|
|
_m3d_getfloat(ptr, &w); f = w; |
3021 |
|
|
memcpy(&h->cmd[j].arg[k], &f, 4); |
3022 |
|
|
#else |
3023 |
|
|
_m3d_getfloat(ptr, (float*)&h->cmd[j].arg[k]); |
3024 |
|
|
#endif |
3025 |
|
|
break; |
3026 |
|
|
case m3dcp_va_t: |
3027 |
|
|
ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]); |
3028 |
|
|
n = k + 1; l += (h->cmd[j].arg[k] - 1) * (cd->p - k - 1); |
3029 |
|
|
h->cmd[j].arg = (uint32_t*)M3D_REALLOC(h->cmd[j].arg, l * sizeof(uint32_t)); |
3030 |
|
|
if(!h->cmd[j].arg) goto memerr; |
3031 |
|
|
memset(&h->cmd[j].arg[k + 1], 0, (l - k - 1) * sizeof(uint32_t)); |
3032 |
|
|
break; |
3033 |
|
|
case m3dcp_qi_t: |
3034 |
|
|
ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]); |
3035 |
|
|
model->vertex[h->cmd[i].arg[k]].skinid = M3D_INDEXMAX; |
3036 |
|
|
break; |
3037 |
|
|
default: |
3038 |
|
|
ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]); |
3039 |
|
|
break; |
3040 |
|
|
} |
3041 |
|
|
} |
3042 |
|
|
} else { |
3043 |
|
|
M3D_LOG("Unknown shape command in"); |
3044 |
|
|
M3D_LOG(h->name); |
3045 |
|
|
model->errcode = M3D_ERR_UNKCMD; |
3046 |
|
|
} |
3047 |
|
|
ptr = _m3d_findnl(ptr); |
3048 |
|
|
} |
3049 |
|
|
if(!h->numcmd) model->numshape--; |
3050 |
|
|
} else |
3051 |
|
|
/* annotation labels */ |
3052 |
|
|
if(!memcmp(pe, "Labels", 6)) { |
3053 |
|
|
pe = _m3d_findarg(pe); |
3054 |
|
|
if(!*pe) goto asciiend; |
3055 |
|
|
if(*pe == '\r' || *pe == '\n') pe = NULL; |
3056 |
|
|
else pe = _m3d_safestr(pe, 0); |
3057 |
|
|
k = 0; fn = NULL; |
3058 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
3059 |
|
|
if(*ptr == 'c') { |
3060 |
|
|
ptr = _m3d_findarg(ptr); |
3061 |
|
|
if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; |
3062 |
|
|
ptr = _m3d_gethex(ptr, &k); |
3063 |
|
|
} else |
3064 |
|
|
if(*ptr == 'l') { |
3065 |
|
|
ptr = _m3d_findarg(ptr); |
3066 |
|
|
if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; |
3067 |
|
|
fn = _m3d_safestr(ptr, 2); |
3068 |
|
|
} else { |
3069 |
|
|
i = model->numlabel++; |
3070 |
|
|
model->label = (m3dl_t*)M3D_REALLOC(model->label, model->numlabel * sizeof(m3dl_t)); |
3071 |
|
|
if(!model->label) goto memerr; |
3072 |
|
|
model->label[i].name = pe; |
3073 |
|
|
model->label[i].lang = fn; |
3074 |
|
|
model->label[i].color = k; |
3075 |
|
|
ptr = _m3d_getint(ptr, &j); |
3076 |
|
|
model->label[i].vertexid = (M3D_INDEX)j; |
3077 |
|
|
ptr = _m3d_findarg(ptr); |
3078 |
|
|
if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; |
3079 |
|
|
model->label[i].text = _m3d_safestr(ptr, 2); |
3080 |
|
|
} |
3081 |
|
|
ptr = _m3d_findnl(ptr); |
3082 |
|
|
} |
3083 |
|
|
} else |
3084 |
|
|
/* action */ |
3085 |
|
|
if(!memcmp(pe, "Action", 6)) { |
3086 |
|
|
pe = _m3d_findarg(pe); |
3087 |
|
|
if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; |
3088 |
|
|
pe = _m3d_getint(pe, &k); |
3089 |
|
|
pe = _m3d_findarg(pe); |
3090 |
|
|
if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; |
3091 |
|
|
pe = _m3d_safestr(pe, 0); |
3092 |
|
|
if(!pe || !*pe) goto asciiend; |
3093 |
|
|
i = model->numaction++; |
3094 |
|
|
model->action = (m3da_t*)M3D_REALLOC(model->action, model->numaction * sizeof(m3da_t)); |
3095 |
|
|
if(!model->action) goto memerr; |
3096 |
|
|
a = &model->action[i]; |
3097 |
|
|
a->name = pe; |
3098 |
|
|
a->durationmsec = k; |
3099 |
|
|
/* skip the first frame marker as there's always at least one frame */ |
3100 |
|
|
a->numframe = 1; |
3101 |
|
|
a->frame = (m3dfr_t*)M3D_MALLOC(sizeof(m3dfr_t)); |
3102 |
|
|
if(!a->frame) goto memerr; |
3103 |
|
|
a->frame[0].msec = 0; |
3104 |
|
|
a->frame[0].numtransform = 0; |
3105 |
|
|
a->frame[0].transform = NULL; |
3106 |
|
|
i = 0; |
3107 |
|
|
if(*ptr == 'f') |
3108 |
|
|
ptr = _m3d_findnl(ptr); |
3109 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
3110 |
|
|
if(*ptr == 'f') { |
3111 |
|
|
i = a->numframe++; |
3112 |
|
|
a->frame = (m3dfr_t*)M3D_REALLOC(a->frame, a->numframe * sizeof(m3dfr_t)); |
3113 |
|
|
if(!a->frame) goto memerr; |
3114 |
|
|
ptr = _m3d_findarg(ptr); |
3115 |
|
|
ptr = _m3d_getint(ptr, &a->frame[i].msec); |
3116 |
|
|
a->frame[i].numtransform = 0; |
3117 |
|
|
a->frame[i].transform = NULL; |
3118 |
|
|
} else { |
3119 |
|
|
j = a->frame[i].numtransform++; |
3120 |
|
|
a->frame[i].transform = (m3dtr_t*)M3D_REALLOC(a->frame[i].transform, |
3121 |
|
|
a->frame[i].numtransform * sizeof(m3dtr_t)); |
3122 |
|
|
if(!a->frame[i].transform) goto memerr; |
3123 |
|
|
ptr = _m3d_getint(ptr, &k); |
3124 |
|
|
ptr = _m3d_findarg(ptr); |
3125 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
3126 |
|
|
a->frame[i].transform[j].boneid = (M3D_INDEX)k; |
3127 |
|
|
ptr = _m3d_getint(ptr, &k); |
3128 |
|
|
ptr = _m3d_findarg(ptr); |
3129 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
3130 |
|
|
a->frame[i].transform[j].pos = (M3D_INDEX)k; |
3131 |
|
|
ptr = _m3d_getint(ptr, &k); |
3132 |
|
|
if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend; |
3133 |
|
|
a->frame[i].transform[j].ori = (M3D_INDEX)k; |
3134 |
|
|
model->vertex[k].skinid = M3D_INDEXMAX; |
3135 |
|
|
} |
3136 |
|
|
ptr = _m3d_findnl(ptr); |
3137 |
|
|
} |
3138 |
|
|
} else |
3139 |
|
|
/* inlined assets chunk */ |
3140 |
|
|
if(!memcmp(pe, "Assets", 6)) { |
3141 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
3142 |
|
|
if(readfilecb) { |
3143 |
|
|
pe = _m3d_safestr(ptr, 2); |
3144 |
|
|
if(!pe || !*pe) goto asciiend; |
3145 |
|
|
i = model->numinlined++; |
3146 |
|
|
model->inlined = (m3di_t*)M3D_REALLOC(model->inlined, model->numinlined * sizeof(m3di_t)); |
3147 |
|
|
if(!model->inlined) goto memerr; |
3148 |
|
|
t = &model->inlined[i]; |
3149 |
|
|
model->inlined[i].data = (*readfilecb)(pe, &model->inlined[i].length); |
3150 |
|
|
if(model->inlined[i].data) { |
3151 |
|
|
fn = strrchr(pe, '.'); |
3152 |
|
|
if(fn && (fn[1] == 'p' || fn[1] == 'P') && (fn[2] == 'n' || fn[2] == 'N') && |
3153 |
|
|
(fn[3] == 'g' || fn[3] == 'G')) *fn = 0; |
3154 |
|
|
fn = strrchr(pe, '/'); |
3155 |
|
|
if(!fn) fn = strrchr(pe, '\\'); |
3156 |
|
|
if(!fn) fn = pe; else fn++; |
3157 |
|
|
model->inlined[i].name = _m3d_safestr(fn, 0); |
3158 |
|
|
} else |
3159 |
|
|
model->numinlined--; |
3160 |
|
|
M3D_FREE(pe); |
3161 |
|
|
} |
3162 |
|
|
ptr = _m3d_findnl(ptr); |
3163 |
|
|
} |
3164 |
|
|
} else |
3165 |
|
|
/* extra chunks */ |
3166 |
|
|
if(!memcmp(pe, "Extra", 5)) { |
3167 |
|
|
pe = _m3d_findarg(pe); |
3168 |
|
|
if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend; |
3169 |
|
|
buff = (unsigned char*)_m3d_findnl(ptr); |
3170 |
|
|
k = ((uint32_t)((uintptr_t)buff - (uintptr_t)ptr) / 3) + 1; |
3171 |
|
|
i = model->numextra++; |
3172 |
|
|
model->extra = (m3dchunk_t**)M3D_REALLOC(model->extra, model->numextra * sizeof(m3dchunk_t*)); |
3173 |
|
|
if(!model->extra) goto memerr; |
3174 |
|
|
model->extra[i] = (m3dchunk_t*)M3D_MALLOC(k + sizeof(m3dchunk_t)); |
3175 |
|
|
if(!model->extra[i]) goto memerr; |
3176 |
|
|
memcpy(&model->extra[i]->magic, pe, 4); |
3177 |
|
|
model->extra[i]->length = sizeof(m3dchunk_t); |
3178 |
|
|
pe = (char*)model->extra[i] + sizeof(m3dchunk_t); |
3179 |
|
|
while(*ptr && *ptr != '\r' && *ptr != '\n') { |
3180 |
|
|
ptr = _m3d_gethex(ptr, &k); |
3181 |
|
|
*pe++ = (uint8_t)k; |
3182 |
|
|
model->extra[i]->length++; |
3183 |
|
|
} |
3184 |
|
|
} else |
3185 |
|
|
goto asciiend; |
3186 |
|
|
} |
3187 |
|
|
model->errcode = M3D_SUCCESS; |
3188 |
|
|
asciiend: |
3189 |
|
|
setlocale(LC_NUMERIC, ol); |
3190 |
|
|
goto postprocess; |
3191 |
|
|
} |
3192 |
|
|
#endif |
3193 |
|
|
/* Binary variant */ |
3194 |
|
✗ |
len = ((m3dhdr_t*)data)->length - 8; |
3195 |
|
✗ |
data += 8; |
3196 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'P','R','V','W')) { |
3197 |
|
|
/* optional preview chunk */ |
3198 |
|
✗ |
model->preview.length = ((m3dchunk_t*)data)->length; |
3199 |
|
✗ |
model->preview.data = data + sizeof(m3dchunk_t); |
3200 |
|
✗ |
data += model->preview.length; |
3201 |
|
✗ |
len -= model->preview.length; |
3202 |
|
|
} |
3203 |
|
✗ |
if(!M3D_CHUNKMAGIC(data, 'H','E','A','D')) { |
3204 |
|
✗ |
buff = (unsigned char *)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)data, len, 4096, (int*)&len, 1); |
3205 |
|
✗ |
if(!buff || !len || !M3D_CHUNKMAGIC(buff, 'H','E','A','D')) { |
3206 |
|
✗ |
if(buff) M3D_FREE(buff); |
3207 |
|
✗ |
M3D_FREE(model); |
3208 |
|
✗ |
return NULL; |
3209 |
|
|
} |
3210 |
|
✗ |
buff = (unsigned char*)M3D_REALLOC(buff, len); |
3211 |
|
✗ |
model->flags |= M3D_FLG_FREERAW; /* mark that we have to free the raw buffer */ |
3212 |
|
|
data = buff; |
3213 |
|
|
#ifdef M3D_PROFILING |
3214 |
|
|
gettimeofday(&tv1, NULL); |
3215 |
|
|
tvd.tv_sec = tv1.tv_sec - tv0.tv_sec; |
3216 |
|
|
tvd.tv_usec = tv1.tv_usec - tv0.tv_usec; |
3217 |
|
|
if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; } |
3218 |
|
|
printf(" Deflate model %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec); |
3219 |
|
|
memcpy(&tv0, &tv1, sizeof(struct timeval)); |
3220 |
|
|
#endif |
3221 |
|
|
} |
3222 |
|
✗ |
model->raw = (m3dhdr_t*)data; |
3223 |
|
✗ |
end = data + len; |
3224 |
|
|
|
3225 |
|
|
/* parse header */ |
3226 |
|
✗ |
data += sizeof(m3dhdr_t); |
3227 |
|
|
M3D_LOG((char*)data); |
3228 |
|
✗ |
model->name = (char*)data; |
3229 |
|
✗ |
for(; data < end && *data; data++) {}; data++; |
3230 |
|
✗ |
model->license = (char*)data; |
3231 |
|
✗ |
for(; data < end && *data; data++) {}; data++; |
3232 |
|
✗ |
model->author = (char*)data; |
3233 |
|
✗ |
for(; data < end && *data; data++) {}; data++; |
3234 |
|
✗ |
model->desc = (char*)data; |
3235 |
|
✗ |
chunk = (unsigned char*)model->raw + model->raw->length; |
3236 |
|
✗ |
model->scale = (M3D_FLOAT)model->raw->scale; |
3237 |
|
✗ |
if(model->scale <= (M3D_FLOAT)0.0) model->scale = (M3D_FLOAT)1.0; |
3238 |
|
✗ |
model->vc_s = 1 << ((model->raw->types >> 0) & 3); /* vertex coordinate size */ |
3239 |
|
✗ |
model->vi_s = 1 << ((model->raw->types >> 2) & 3); /* vertex index size */ |
3240 |
|
✗ |
model->si_s = 1 << ((model->raw->types >> 4) & 3); /* string offset size */ |
3241 |
|
✗ |
model->ci_s = 1 << ((model->raw->types >> 6) & 3); /* color index size */ |
3242 |
|
✗ |
model->ti_s = 1 << ((model->raw->types >> 8) & 3); /* tmap index size */ |
3243 |
|
✗ |
model->bi_s = 1 << ((model->raw->types >>10) & 3); /* bone index size */ |
3244 |
|
✗ |
model->nb_s = 1 << ((model->raw->types >>12) & 3); /* number of bones per vertex */ |
3245 |
|
✗ |
model->sk_s = 1 << ((model->raw->types >>14) & 3); /* skin index size */ |
3246 |
|
✗ |
model->fc_s = 1 << ((model->raw->types >>16) & 3); /* frame counter size */ |
3247 |
|
✗ |
model->hi_s = 1 << ((model->raw->types >>18) & 3); /* shape index size */ |
3248 |
|
✗ |
model->fi_s = 1 << ((model->raw->types >>20) & 3); /* face index size */ |
3249 |
|
✗ |
model->vd_s = 1 << ((model->raw->types >>22) & 3); /* voxel dimension size */ |
3250 |
|
✗ |
model->vp_s = 1 << ((model->raw->types >>24) & 3); /* voxel pixel size */ |
3251 |
|
✗ |
if(model->ci_s == 8) model->ci_s = 0; /* optional indices */ |
3252 |
|
✗ |
if(model->ti_s == 8) model->ti_s = 0; |
3253 |
|
✗ |
if(model->bi_s == 8) model->bi_s = 0; |
3254 |
|
✗ |
if(model->sk_s == 8) model->sk_s = 0; |
3255 |
|
✗ |
if(model->fc_s == 8) model->fc_s = 0; |
3256 |
|
✗ |
if(model->hi_s == 8) model->hi_s = 0; |
3257 |
|
✗ |
if(model->fi_s == 8) model->fi_s = 0; |
3258 |
|
|
|
3259 |
|
|
/* variable limit checks */ |
3260 |
|
✗ |
if(sizeof(M3D_FLOAT) == 4 && model->vc_s > 4) { |
3261 |
|
|
M3D_LOG("Double precision coordinates not supported, truncating to float..."); |
3262 |
|
✗ |
model->errcode = M3D_ERR_TRUNC; |
3263 |
|
|
} |
3264 |
|
✗ |
if((sizeof(M3D_INDEX) == 2 && (model->vi_s > 2 || model->si_s > 2 || model->ci_s > 2 || model->ti_s > 2 || |
3265 |
|
✗ |
model->bi_s > 2 || model->sk_s > 2 || model->fc_s > 2 || model->hi_s > 2 || model->fi_s > 2)) || |
3266 |
|
✗ |
(sizeof(M3D_VOXEL) < (size_t)model->vp_s && model->vp_s != 8)) { |
3267 |
|
|
M3D_LOG("32 bit indices not supported, unable to load model"); |
3268 |
|
✗ |
M3D_FREE(model); |
3269 |
|
✗ |
return NULL; |
3270 |
|
|
} |
3271 |
|
✗ |
if(model->vi_s > 4 || model->si_s > 4 || model->vp_s == 4) { |
3272 |
|
|
M3D_LOG("Invalid index size, unable to load model"); |
3273 |
|
✗ |
M3D_FREE(model); |
3274 |
|
✗ |
return NULL; |
3275 |
|
|
} |
3276 |
|
✗ |
if(!M3D_CHUNKMAGIC(end - 4, 'O','M','D','3')) { |
3277 |
|
|
M3D_LOG("Missing end chunk"); |
3278 |
|
✗ |
M3D_FREE(model); |
3279 |
|
✗ |
return NULL; |
3280 |
|
|
} |
3281 |
|
✗ |
if(model->nb_s > M3D_NUMBONE) { |
3282 |
|
|
M3D_LOG("Model has more bones per vertex than what importer was configured to support"); |
3283 |
|
✗ |
model->errcode = M3D_ERR_TRUNC; |
3284 |
|
|
} |
3285 |
|
|
|
3286 |
|
|
/* look for inlined assets in advance, material and procedural chunks may need them */ |
3287 |
|
|
buff = chunk; |
3288 |
|
✗ |
while(buff < end && !M3D_CHUNKMAGIC(buff, 'O','M','D','3')) { |
3289 |
|
|
data = buff; |
3290 |
|
✗ |
len = ((m3dchunk_t*)data)->length; |
3291 |
|
✗ |
buff += len; |
3292 |
|
✗ |
if(len < sizeof(m3dchunk_t) || buff >= end) { |
3293 |
|
|
M3D_LOG("Invalid chunk size"); |
3294 |
|
|
break; |
3295 |
|
|
} |
3296 |
|
✗ |
len -= sizeof(m3dchunk_t) + model->si_s; |
3297 |
|
|
|
3298 |
|
|
/* inlined assets */ |
3299 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'A','S','E','T') && len > 0) { |
3300 |
|
|
M3D_LOG("Inlined asset"); |
3301 |
|
✗ |
i = model->numinlined++; |
3302 |
|
✗ |
model->inlined = (m3di_t*)M3D_REALLOC(model->inlined, model->numinlined * sizeof(m3di_t)); |
3303 |
|
✗ |
if(!model->inlined) { |
3304 |
|
✗ |
memerr: M3D_LOG("Out of memory"); |
3305 |
|
✗ |
model->errcode = M3D_ERR_ALLOC; |
3306 |
|
✗ |
return model; |
3307 |
|
|
} |
3308 |
|
✗ |
data += sizeof(m3dchunk_t); |
3309 |
|
✗ |
t = &model->inlined[i]; |
3310 |
|
✗ |
M3D_GETSTR(t->name); |
3311 |
|
|
M3D_LOG(t->name); |
3312 |
|
✗ |
t->data = (uint8_t*)data; |
3313 |
|
✗ |
t->length = len; |
3314 |
|
|
} |
3315 |
|
|
} |
3316 |
|
|
|
3317 |
|
|
/* parse chunks */ |
3318 |
|
✗ |
while(chunk < end && !M3D_CHUNKMAGIC(chunk, 'O','M','D','3')) { |
3319 |
|
|
data = chunk; |
3320 |
|
✗ |
len = ((m3dchunk_t*)chunk)->length; |
3321 |
|
✗ |
chunk += len; |
3322 |
|
✗ |
if(len < sizeof(m3dchunk_t) || chunk >= end) { |
3323 |
|
|
M3D_LOG("Invalid chunk size"); |
3324 |
|
|
break; |
3325 |
|
|
} |
3326 |
|
✗ |
len -= sizeof(m3dchunk_t); |
3327 |
|
|
|
3328 |
|
|
/* color map */ |
3329 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'C','M','A','P')) { |
3330 |
|
|
M3D_LOG("Color map"); |
3331 |
|
✗ |
if(model->cmap) { M3D_LOG("More color map chunks, should be unique"); model->errcode = M3D_ERR_CMAP; continue; } |
3332 |
|
✗ |
if(!model->ci_s) { M3D_LOG("Color map chunk, shouldn't be any"); model->errcode = M3D_ERR_CMAP; continue; } |
3333 |
|
✗ |
model->numcmap = len / sizeof(uint32_t); |
3334 |
|
✗ |
model->cmap = (uint32_t*)(data + sizeof(m3dchunk_t)); |
3335 |
|
|
} else |
3336 |
|
|
/* texture map */ |
3337 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'T','M','A','P')) { |
3338 |
|
|
M3D_LOG("Texture map"); |
3339 |
|
✗ |
if(model->tmap) { M3D_LOG("More texture map chunks, should be unique"); model->errcode = M3D_ERR_TMAP; continue; } |
3340 |
|
✗ |
if(!model->ti_s) { M3D_LOG("Texture map chunk, shouldn't be any"); model->errcode = M3D_ERR_TMAP; continue; } |
3341 |
|
✗ |
reclen = model->vc_s + model->vc_s; |
3342 |
|
✗ |
model->numtmap = len / reclen; |
3343 |
|
✗ |
model->tmap = (m3dti_t*)M3D_MALLOC(model->numtmap * sizeof(m3dti_t)); |
3344 |
|
✗ |
if(!model->tmap) goto memerr; |
3345 |
|
✗ |
for(i = 0, data += sizeof(m3dchunk_t); data < chunk; i++) { |
3346 |
|
✗ |
switch(model->vc_s) { |
3347 |
|
✗ |
case 1: |
3348 |
|
✗ |
model->tmap[i].u = (M3D_FLOAT)((uint8_t)data[0]) / (M3D_FLOAT)255.0; |
3349 |
|
✗ |
model->tmap[i].v = (M3D_FLOAT)((uint8_t)data[1]) / (M3D_FLOAT)255.0; |
3350 |
|
✗ |
break; |
3351 |
|
✗ |
case 2: |
3352 |
|
✗ |
model->tmap[i].u = (M3D_FLOAT)(*((uint16_t*)(data+0))) / (M3D_FLOAT)65535.0; |
3353 |
|
✗ |
model->tmap[i].v = (M3D_FLOAT)(*((uint16_t*)(data+2))) / (M3D_FLOAT)65535.0; |
3354 |
|
✗ |
break; |
3355 |
|
✗ |
case 4: |
3356 |
|
✗ |
model->tmap[i].u = (M3D_FLOAT)(*((float*)(data+0))); |
3357 |
|
✗ |
model->tmap[i].v = (M3D_FLOAT)(*((float*)(data+4))); |
3358 |
|
✗ |
break; |
3359 |
|
✗ |
case 8: |
3360 |
|
✗ |
model->tmap[i].u = (M3D_FLOAT)(*((double*)(data+0))); |
3361 |
|
✗ |
model->tmap[i].v = (M3D_FLOAT)(*((double*)(data+8))); |
3362 |
|
✗ |
break; |
3363 |
|
|
} |
3364 |
|
✗ |
data += reclen; |
3365 |
|
|
} |
3366 |
|
|
} else |
3367 |
|
|
/* vertex list */ |
3368 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'V','R','T','S')) { |
3369 |
|
|
M3D_LOG("Vertex list"); |
3370 |
|
✗ |
if(model->vertex) { M3D_LOG("More vertex chunks, should be unique"); model->errcode = M3D_ERR_VRTS; continue; } |
3371 |
|
✗ |
if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP; |
3372 |
|
✗ |
reclen = model->ci_s + model->sk_s + 4 * model->vc_s; |
3373 |
|
✗ |
model->numvertex = len / reclen; |
3374 |
|
✗ |
model->vertex = (m3dv_t*)M3D_MALLOC(model->numvertex * sizeof(m3dv_t)); |
3375 |
|
✗ |
if(!model->vertex) goto memerr; |
3376 |
|
|
memset(model->vertex, 0, model->numvertex * sizeof(m3dv_t)); |
3377 |
|
✗ |
for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < model->numvertex; i++) { |
3378 |
|
✗ |
switch(model->vc_s) { |
3379 |
|
✗ |
case 1: |
3380 |
|
✗ |
model->vertex[i].x = (M3D_FLOAT)((int8_t)data[0]) / (M3D_FLOAT)127.0; |
3381 |
|
✗ |
model->vertex[i].y = (M3D_FLOAT)((int8_t)data[1]) / (M3D_FLOAT)127.0; |
3382 |
|
✗ |
model->vertex[i].z = (M3D_FLOAT)((int8_t)data[2]) / (M3D_FLOAT)127.0; |
3383 |
|
✗ |
model->vertex[i].w = (M3D_FLOAT)((int8_t)data[3]) / (M3D_FLOAT)127.0; |
3384 |
|
✗ |
data += 4; |
3385 |
|
✗ |
break; |
3386 |
|
✗ |
case 2: |
3387 |
|
✗ |
model->vertex[i].x = (M3D_FLOAT)(*((int16_t*)(data+0))) / (M3D_FLOAT)32767.0; |
3388 |
|
✗ |
model->vertex[i].y = (M3D_FLOAT)(*((int16_t*)(data+2))) / (M3D_FLOAT)32767.0; |
3389 |
|
✗ |
model->vertex[i].z = (M3D_FLOAT)(*((int16_t*)(data+4))) / (M3D_FLOAT)32767.0; |
3390 |
|
✗ |
model->vertex[i].w = (M3D_FLOAT)(*((int16_t*)(data+6))) / (M3D_FLOAT)32767.0; |
3391 |
|
✗ |
data += 8; |
3392 |
|
✗ |
break; |
3393 |
|
✗ |
case 4: |
3394 |
|
✗ |
model->vertex[i].x = (M3D_FLOAT)(*((float*)(data+0))); |
3395 |
|
✗ |
model->vertex[i].y = (M3D_FLOAT)(*((float*)(data+4))); |
3396 |
|
✗ |
model->vertex[i].z = (M3D_FLOAT)(*((float*)(data+8))); |
3397 |
|
✗ |
model->vertex[i].w = (M3D_FLOAT)(*((float*)(data+12))); |
3398 |
|
✗ |
data += 16; |
3399 |
|
✗ |
break; |
3400 |
|
✗ |
case 8: |
3401 |
|
✗ |
model->vertex[i].x = (M3D_FLOAT)(*((double*)(data+0))); |
3402 |
|
✗ |
model->vertex[i].y = (M3D_FLOAT)(*((double*)(data+8))); |
3403 |
|
✗ |
model->vertex[i].z = (M3D_FLOAT)(*((double*)(data+16))); |
3404 |
|
✗ |
model->vertex[i].w = (M3D_FLOAT)(*((double*)(data+24))); |
3405 |
|
✗ |
data += 32; |
3406 |
|
✗ |
break; |
3407 |
|
|
} |
3408 |
|
✗ |
switch(model->ci_s) { |
3409 |
|
✗ |
case 1: model->vertex[i].color = model->cmap ? model->cmap[data[0]] : 0; data++; break; |
3410 |
|
✗ |
case 2: model->vertex[i].color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break; |
3411 |
|
✗ |
case 4: model->vertex[i].color = *((uint32_t*)data); data += 4; break; |
3412 |
|
|
/* case 8: break; */ |
3413 |
|
|
} |
3414 |
|
✗ |
model->vertex[i].skinid = M3D_UNDEF; |
3415 |
|
✗ |
data = _m3d_getidx(data, model->sk_s, &model->vertex[i].skinid); |
3416 |
|
|
} |
3417 |
|
|
} else |
3418 |
|
|
/* skeleton: bone hierarchy and skin */ |
3419 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'B','O','N','E')) { |
3420 |
|
|
M3D_LOG("Skeleton"); |
3421 |
|
✗ |
if(model->bone) { M3D_LOG("More bone chunks, should be unique"); model->errcode = M3D_ERR_BONE; continue; } |
3422 |
|
✗ |
if(!model->bi_s) { M3D_LOG("Bone chunk, shouldn't be any"); model->errcode=M3D_ERR_BONE; continue; } |
3423 |
|
✗ |
if(!model->vertex) { M3D_LOG("No vertex chunk before bones"); model->errcode = M3D_ERR_VRTS; break; } |
3424 |
|
✗ |
data += sizeof(m3dchunk_t); |
3425 |
|
✗ |
model->numbone = 0; |
3426 |
|
|
data = _m3d_getidx(data, model->bi_s, &model->numbone); |
3427 |
|
✗ |
if(model->numbone) { |
3428 |
|
✗ |
model->bone = (m3db_t*)M3D_MALLOC(model->numbone * sizeof(m3db_t)); |
3429 |
|
✗ |
if(!model->bone) goto memerr; |
3430 |
|
|
} |
3431 |
|
✗ |
model->numskin = 0; |
3432 |
|
✗ |
data = _m3d_getidx(data, model->sk_s, &model->numskin); |
3433 |
|
|
/* read bone hierarchy */ |
3434 |
|
✗ |
for(i = 0; data < chunk && i < model->numbone; i++) { |
3435 |
|
✗ |
data = _m3d_getidx(data, model->bi_s, &model->bone[i].parent); |
3436 |
|
✗ |
M3D_GETSTR(model->bone[i].name); |
3437 |
|
✗ |
data = _m3d_getidx(data, model->vi_s, &model->bone[i].pos); |
3438 |
|
✗ |
data = _m3d_getidx(data, model->vi_s, &model->bone[i].ori); |
3439 |
|
✗ |
model->bone[i].numweight = 0; |
3440 |
|
✗ |
model->bone[i].weight = NULL; |
3441 |
|
|
} |
3442 |
|
|
/* read skin definitions */ |
3443 |
|
✗ |
if(model->numskin) { |
3444 |
|
✗ |
model->skin = (m3ds_t*)M3D_MALLOC(model->numskin * sizeof(m3ds_t)); |
3445 |
|
✗ |
if(!model->skin) goto memerr; |
3446 |
|
✗ |
for(i = 0; data < chunk && i < model->numskin; i++) { |
3447 |
|
✗ |
for(j = 0; j < M3D_NUMBONE; j++) { |
3448 |
|
✗ |
model->skin[i].boneid[j] = M3D_UNDEF; |
3449 |
|
✗ |
model->skin[i].weight[j] = (M3D_FLOAT)0.0; |
3450 |
|
|
} |
3451 |
|
|
memset(&weights, 0, sizeof(weights)); |
3452 |
|
✗ |
if(model->nb_s == 1) weights[0] = 255; |
3453 |
|
|
else { |
3454 |
|
✗ |
memcpy(&weights, data, model->nb_s); |
3455 |
|
✗ |
data += model->nb_s; |
3456 |
|
|
} |
3457 |
|
✗ |
for(j = 0, w = (M3D_FLOAT)0.0; j < (unsigned int)model->nb_s; j++) { |
3458 |
|
✗ |
if(weights[j]) { |
3459 |
|
✗ |
if(j >= M3D_NUMBONE) |
3460 |
|
✗ |
data += model->bi_s; |
3461 |
|
|
else { |
3462 |
|
✗ |
model->skin[i].weight[j] = (M3D_FLOAT)(weights[j]) / (M3D_FLOAT)255.0; |
3463 |
|
✗ |
w += model->skin[i].weight[j]; |
3464 |
|
✗ |
data = _m3d_getidx(data, model->bi_s, &model->skin[i].boneid[j]); |
3465 |
|
|
} |
3466 |
|
|
} |
3467 |
|
|
} |
3468 |
|
|
/* this can occur if model has more bones than what the importer is configured to handle */ |
3469 |
|
✗ |
if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0) { |
3470 |
|
✗ |
for(j = 0; j < M3D_NUMBONE; j++) |
3471 |
|
✗ |
model->skin[i].weight[j] /= w; |
3472 |
|
|
} |
3473 |
|
|
} |
3474 |
|
|
} |
3475 |
|
|
} else |
3476 |
|
|
/* material */ |
3477 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'M','T','R','L')) { |
3478 |
|
✗ |
data += sizeof(m3dchunk_t); |
3479 |
|
✗ |
M3D_GETSTR(name); |
3480 |
|
|
M3D_LOG("Material"); |
3481 |
|
|
M3D_LOG(name); |
3482 |
|
✗ |
if(model->ci_s < 4 && !model->numcmap) model->errcode = M3D_ERR_CMAP; |
3483 |
|
✗ |
for(i = 0; i < model->nummaterial; i++) |
3484 |
|
✗ |
if(!strcmp(name, model->material[i].name)) { |
3485 |
|
✗ |
model->errcode = M3D_ERR_MTRL; |
3486 |
|
|
M3D_LOG("Multiple definitions for material"); |
3487 |
|
|
M3D_LOG(name); |
3488 |
|
|
name = NULL; |
3489 |
|
|
break; |
3490 |
|
|
} |
3491 |
|
✗ |
if(name) { |
3492 |
|
✗ |
i = model->nummaterial++; |
3493 |
|
✗ |
if(model->flags & M3D_FLG_MTLLIB) { |
3494 |
|
✗ |
m = model->material; |
3495 |
|
✗ |
model->material = (m3dm_t*)M3D_MALLOC(model->nummaterial * sizeof(m3dm_t)); |
3496 |
|
✗ |
if(!model->material) goto memerr; |
3497 |
|
✗ |
memcpy(model->material, m, (model->nummaterial - 1) * sizeof(m3dm_t)); |
3498 |
|
✗ |
if(model->texture) { |
3499 |
|
|
tx = model->texture; |
3500 |
|
✗ |
model->texture = (m3dtx_t*)M3D_MALLOC(model->numtexture * sizeof(m3dtx_t)); |
3501 |
|
✗ |
if(!model->texture) goto memerr; |
3502 |
|
|
memcpy(model->texture, tx, model->numtexture * sizeof(m3dm_t)); |
3503 |
|
|
} |
3504 |
|
✗ |
model->flags &= ~M3D_FLG_MTLLIB; |
3505 |
|
|
} else { |
3506 |
|
✗ |
model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t)); |
3507 |
|
✗ |
if(!model->material) goto memerr; |
3508 |
|
|
} |
3509 |
|
✗ |
m = &model->material[i]; |
3510 |
|
✗ |
m->numprop = 0; |
3511 |
|
✗ |
m->name = name; |
3512 |
|
✗ |
m->prop = (m3dp_t*)M3D_MALLOC((len / 2) * sizeof(m3dp_t)); |
3513 |
|
✗ |
if(!m->prop) goto memerr; |
3514 |
|
✗ |
while(data < chunk) { |
3515 |
|
✗ |
i = m->numprop++; |
3516 |
|
✗ |
m->prop[i].type = *data++; |
3517 |
|
✗ |
m->prop[i].value.num = 0; |
3518 |
|
✗ |
if(m->prop[i].type >= 128) |
3519 |
|
|
k = m3dpf_map; |
3520 |
|
|
else { |
3521 |
|
✗ |
for(k = 256, j = 0; j < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); j++) |
3522 |
|
✗ |
if(m->prop[i].type == m3d_propertytypes[j].id) { k = m3d_propertytypes[j].format; break; } |
3523 |
|
|
} |
3524 |
|
✗ |
switch(k) { |
3525 |
|
✗ |
case m3dpf_color: |
3526 |
|
✗ |
switch(model->ci_s) { |
3527 |
|
✗ |
case 1: m->prop[i].value.color = model->cmap ? model->cmap[data[0]] : 0; data++; break; |
3528 |
|
✗ |
case 2: m->prop[i].value.color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break; |
3529 |
|
✗ |
case 4: m->prop[i].value.color = *((uint32_t*)data); data += 4; break; |
3530 |
|
|
} |
3531 |
|
|
break; |
3532 |
|
|
|
3533 |
|
✗ |
case m3dpf_uint8: m->prop[i].value.num = *data++; break; |
3534 |
|
✗ |
case m3dpf_uint16:m->prop[i].value.num = *((uint16_t*)data); data += 2; break; |
3535 |
|
✗ |
case m3dpf_uint32:m->prop[i].value.num = *((uint32_t*)data); data += 4; break; |
3536 |
|
✗ |
case m3dpf_float: m->prop[i].value.fnum = *((float*)data); data += 4; break; |
3537 |
|
|
|
3538 |
|
✗ |
case m3dpf_map: |
3539 |
|
✗ |
M3D_GETSTR(name); |
3540 |
|
✗ |
m->prop[i].value.textureid = _m3d_gettx(model, readfilecb, freecb, name); |
3541 |
|
✗ |
if(model->errcode == M3D_ERR_ALLOC) goto memerr; |
3542 |
|
|
/* this error code only returned if readfilecb was specified */ |
3543 |
|
✗ |
if(m->prop[i].value.textureid == M3D_UNDEF) { |
3544 |
|
|
M3D_LOG("Texture not found"); |
3545 |
|
|
M3D_LOG(m->name); |
3546 |
|
✗ |
m->numprop--; |
3547 |
|
|
} |
3548 |
|
|
break; |
3549 |
|
|
|
3550 |
|
✗ |
default: |
3551 |
|
|
M3D_LOG("Unknown material property in"); |
3552 |
|
|
M3D_LOG(m->name); |
3553 |
|
✗ |
model->errcode = M3D_ERR_UNKPROP; |
3554 |
|
|
data = chunk; |
3555 |
|
✗ |
break; |
3556 |
|
|
} |
3557 |
|
|
} |
3558 |
|
✗ |
m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t)); |
3559 |
|
✗ |
if(!m->prop) goto memerr; |
3560 |
|
|
} |
3561 |
|
|
} else |
3562 |
|
|
/* face */ |
3563 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'P','R','O','C')) { |
3564 |
|
|
/* procedural surface */ |
3565 |
|
✗ |
M3D_GETSTR(name); |
3566 |
|
|
M3D_LOG("Procedural surface"); |
3567 |
|
|
M3D_LOG(name); |
3568 |
|
✗ |
_m3d_getpr(model, readfilecb, freecb, name); |
3569 |
|
|
} else |
3570 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'M','E','S','H')) { |
3571 |
|
|
M3D_LOG("Mesh data"); |
3572 |
|
✗ |
if(!model->vertex) { M3D_LOG("No vertex chunk before mesh"); model->errcode = M3D_ERR_VRTS; } |
3573 |
|
|
/* mesh */ |
3574 |
|
✗ |
data += sizeof(m3dchunk_t); |
3575 |
|
|
mi = M3D_UNDEF; |
3576 |
|
|
#ifdef M3D_VERTEXMAX |
3577 |
|
|
pi = M3D_UNDEF; |
3578 |
|
|
#endif |
3579 |
|
✗ |
am = model->numface; |
3580 |
|
✗ |
while(data < chunk) { |
3581 |
|
✗ |
k = *data++; |
3582 |
|
✗ |
n = k >> 4; |
3583 |
|
|
k &= 15; |
3584 |
|
✗ |
if(!n) { |
3585 |
|
✗ |
if(!k) { |
3586 |
|
|
/* use material */ |
3587 |
|
|
mi = M3D_UNDEF; |
3588 |
|
✗ |
M3D_GETSTR(name); |
3589 |
|
|
if(name) { |
3590 |
|
✗ |
for(j = 0; j < model->nummaterial; j++) |
3591 |
|
✗ |
if(!strcmp(name, model->material[j].name)) { |
3592 |
|
|
mi = (M3D_INDEX)j; |
3593 |
|
|
break; |
3594 |
|
|
} |
3595 |
|
✗ |
if(mi == M3D_UNDEF) model->errcode = M3D_ERR_MTRL; |
3596 |
|
|
} |
3597 |
|
|
} else { |
3598 |
|
|
/* use parameter */ |
3599 |
|
✗ |
M3D_GETSTR(name); |
3600 |
|
|
#ifdef M3D_VERTEXMAX |
3601 |
|
|
pi = M3D_UNDEF; |
3602 |
|
|
if(name) { |
3603 |
|
|
for(j = 0; j < model->numparam; j++) |
3604 |
|
|
if(!strcmp(name, model->param[j].name)) { |
3605 |
|
|
pi = (M3D_INDEX)j; |
3606 |
|
|
break; |
3607 |
|
|
} |
3608 |
|
|
if(pi == M3D_UNDEF) { |
3609 |
|
|
pi = model->numparam++; |
3610 |
|
|
model->param = (m3dvi_t*)M3D_REALLOC(model->param, model->numparam * sizeof(m3dvi_t)); |
3611 |
|
|
if(!model->param) goto memerr; |
3612 |
|
|
model->param[pi].name = name; |
3613 |
|
|
model->param[pi].count = 0; |
3614 |
|
|
} |
3615 |
|
|
} |
3616 |
|
|
#endif |
3617 |
|
|
} |
3618 |
|
✗ |
continue; |
3619 |
|
|
} |
3620 |
|
✗ |
if(n != 3) { M3D_LOG("Only triangle mesh supported for now"); model->errcode = M3D_ERR_UNKMESH; return model; } |
3621 |
|
✗ |
i = model->numface++; |
3622 |
|
✗ |
if(model->numface > am) { |
3623 |
|
✗ |
am = model->numface + 4095; |
3624 |
|
✗ |
model->face = (m3df_t*)M3D_REALLOC(model->face, am * sizeof(m3df_t)); |
3625 |
|
✗ |
if(!model->face) goto memerr; |
3626 |
|
|
} |
3627 |
|
✗ |
memset(&model->face[i], 255, sizeof(m3df_t)); /* set all index to -1 by default */ |
3628 |
|
✗ |
model->face[i].materialid = mi; |
3629 |
|
|
#ifdef M3D_VERTEXMAX |
3630 |
|
|
model->face[i].paramid = pi; |
3631 |
|
|
#endif |
3632 |
|
✗ |
for(j = 0; data < chunk && j < n; j++) { |
3633 |
|
|
/* vertex */ |
3634 |
|
✗ |
data = _m3d_getidx(data, model->vi_s, &model->face[i].vertex[j]); |
3635 |
|
|
/* texcoord */ |
3636 |
|
✗ |
if(k & 1) |
3637 |
|
✗ |
data = _m3d_getidx(data, model->ti_s, &model->face[i].texcoord[j]); |
3638 |
|
|
/* normal */ |
3639 |
|
✗ |
if(k & 2) |
3640 |
|
✗ |
data = _m3d_getidx(data, model->vi_s, &model->face[i].normal[j]); |
3641 |
|
|
#ifndef M3D_NONORMALS |
3642 |
|
✗ |
if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1; |
3643 |
|
|
#endif |
3644 |
|
|
/* maximum */ |
3645 |
|
✗ |
if(k & 4) |
3646 |
|
|
#ifdef M3D_VERTEXMAX |
3647 |
|
|
data = _m3d_getidx(data, model->vi_s, &model->face[i].vertmax[j]); |
3648 |
|
|
#else |
3649 |
|
✗ |
data += model->vi_s; |
3650 |
|
|
#endif |
3651 |
|
|
} |
3652 |
|
✗ |
if(j != n) { M3D_LOG("Invalid mesh"); model->numface = 0; model->errcode = M3D_ERR_UNKMESH; return model; } |
3653 |
|
|
} |
3654 |
|
✗ |
model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t)); |
3655 |
|
|
} else |
3656 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'V','O','X','T')) { |
3657 |
|
|
/* voxel types */ |
3658 |
|
|
M3D_LOG("Voxel types list"); |
3659 |
|
✗ |
if(model->voxtype) { M3D_LOG("More voxel type chunks, should be unique"); model->errcode = M3D_ERR_VOXT; continue; } |
3660 |
|
✗ |
if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP; |
3661 |
|
✗ |
reclen = model->ci_s + model->si_s + 3 + model->sk_s; |
3662 |
|
✗ |
k = len / reclen; |
3663 |
|
✗ |
model->voxtype = (m3dvt_t*)M3D_MALLOC(k * sizeof(m3dvt_t)); |
3664 |
|
✗ |
if(!model->voxtype) goto memerr; |
3665 |
|
|
memset(model->voxtype, 0, k * sizeof(m3dvt_t)); |
3666 |
|
✗ |
model->numvoxtype = 0; |
3667 |
|
✗ |
for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < k; i++) { |
3668 |
|
✗ |
switch(model->ci_s) { |
3669 |
|
✗ |
case 1: model->voxtype[i].color = model->cmap ? model->cmap[data[0]] : 0; data++; break; |
3670 |
|
✗ |
case 2: model->voxtype[i].color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break; |
3671 |
|
✗ |
case 4: model->voxtype[i].color = *((uint32_t*)data); data += 4; break; |
3672 |
|
|
/* case 8: break; */ |
3673 |
|
|
} |
3674 |
|
✗ |
M3D_GETSTR(name); |
3675 |
|
✗ |
model->voxtype[i].materialid = M3D_UNDEF; |
3676 |
|
✗ |
if(name) { |
3677 |
|
✗ |
model->voxtype[i].name = name; |
3678 |
|
|
/* |
3679 |
|
|
for(j = 0; j < model->nummaterial; j++) |
3680 |
|
|
if(!strcmp(name, model->material[j].name)) { |
3681 |
|
|
model->voxtype[i].materialid = (M3D_INDEX)j; |
3682 |
|
|
break; |
3683 |
|
|
} |
3684 |
|
|
*/ |
3685 |
|
|
} |
3686 |
|
✗ |
j = *data++; |
3687 |
|
✗ |
model->voxtype[i].rotation = j & 0xBF; |
3688 |
|
✗ |
model->voxtype[i].voxshape = ((j & 0x40) << 2) | *data++; |
3689 |
|
✗ |
model->voxtype[i].numitem = *data++; |
3690 |
|
✗ |
model->voxtype[i].skinid = M3D_UNDEF; |
3691 |
|
✗ |
data = _m3d_getidx(data, model->sk_s, &model->voxtype[i].skinid); |
3692 |
|
✗ |
if(model->voxtype[i].numitem) { |
3693 |
|
✗ |
model->voxtype[i].item = (m3dvi_t*)M3D_MALLOC(model->voxtype[i].numitem * sizeof(m3dvi_t)); |
3694 |
|
✗ |
if(!model->voxtype[i].item) goto memerr; |
3695 |
|
✗ |
memset(model->voxtype[i].item, 0, model->voxtype[i].numitem * sizeof(m3dvi_t)); |
3696 |
|
✗ |
for(j = 0; j < model->voxtype[i].numitem; j++) { |
3697 |
|
✗ |
model->voxtype[i].item[j].count = *data++; |
3698 |
|
✗ |
model->voxtype[i].item[j].count |= (*data++) << 8; |
3699 |
|
✗ |
M3D_GETSTR(model->voxtype[i].item[j].name); |
3700 |
|
|
} |
3701 |
|
|
} |
3702 |
|
|
} |
3703 |
|
✗ |
model->numvoxtype = i; |
3704 |
|
✗ |
if(k != model->numvoxtype) { |
3705 |
|
✗ |
model->voxtype = (m3dvt_t*)M3D_REALLOC(model->voxtype, model->numvoxtype * sizeof(m3dvt_t)); |
3706 |
|
✗ |
if(!model->voxtype) goto memerr; |
3707 |
|
|
} |
3708 |
|
|
} else |
3709 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'V','O','X','D')) { |
3710 |
|
|
/* voxel data */ |
3711 |
|
✗ |
data += sizeof(m3dchunk_t); |
3712 |
|
✗ |
M3D_GETSTR(name); |
3713 |
|
|
M3D_LOG("Voxel Data Layer"); |
3714 |
|
|
M3D_LOG(name); |
3715 |
|
✗ |
if(model->vd_s > 4 || model->vp_s > 2) { M3D_LOG("No voxel index size"); model->errcode = M3D_ERR_UNKVOX; continue; } |
3716 |
|
✗ |
if(!model->voxtype) { M3D_LOG("No voxel type chunk before voxel data"); model->errcode = M3D_ERR_VOXT; } |
3717 |
|
✗ |
i = model->numvoxel++; |
3718 |
|
✗ |
model->voxel = (m3dvx_t*)M3D_REALLOC(model->voxel, model->numvoxel * sizeof(m3dvx_t)); |
3719 |
|
✗ |
if(!model->voxel) goto memerr; |
3720 |
|
✗ |
memset(&model->voxel[i], 0, sizeof(m3dvx_t)); |
3721 |
|
✗ |
model->voxel[i].name = name; |
3722 |
|
✗ |
switch(model->vd_s) { |
3723 |
|
✗ |
case 1: |
3724 |
|
✗ |
model->voxel[i].x = (int32_t)((int8_t)data[0]); |
3725 |
|
✗ |
model->voxel[i].y = (int32_t)((int8_t)data[1]); |
3726 |
|
✗ |
model->voxel[i].z = (int32_t)((int8_t)data[2]); |
3727 |
|
✗ |
model->voxel[i].w = (uint32_t)(data[3]); |
3728 |
|
✗ |
model->voxel[i].h = (uint32_t)(data[4]); |
3729 |
|
✗ |
model->voxel[i].d = (uint32_t)(data[5]); |
3730 |
|
✗ |
data += 6; |
3731 |
|
✗ |
break; |
3732 |
|
✗ |
case 2: |
3733 |
|
✗ |
model->voxel[i].x = (int32_t)(*((int16_t*)(data+0))); |
3734 |
|
✗ |
model->voxel[i].y = (int32_t)(*((int16_t*)(data+2))); |
3735 |
|
✗ |
model->voxel[i].z = (int32_t)(*((int16_t*)(data+4))); |
3736 |
|
✗ |
model->voxel[i].w = (uint32_t)(*((uint16_t*)(data+6))); |
3737 |
|
✗ |
model->voxel[i].h = (uint32_t)(*((uint16_t*)(data+8))); |
3738 |
|
✗ |
model->voxel[i].d = (uint32_t)(*((uint16_t*)(data+10))); |
3739 |
|
✗ |
data += 12; |
3740 |
|
✗ |
break; |
3741 |
|
✗ |
case 4: |
3742 |
|
✗ |
model->voxel[i].x = *((int32_t*)(data+0)); |
3743 |
|
✗ |
model->voxel[i].y = *((int32_t*)(data+4)); |
3744 |
|
✗ |
model->voxel[i].z = *((int32_t*)(data+8)); |
3745 |
|
✗ |
model->voxel[i].w = *((uint32_t*)(data+12)); |
3746 |
|
✗ |
model->voxel[i].h = *((uint32_t*)(data+16)); |
3747 |
|
✗ |
model->voxel[i].d = *((uint32_t*)(data+20)); |
3748 |
|
✗ |
data += 24; |
3749 |
|
✗ |
break; |
3750 |
|
|
} |
3751 |
|
✗ |
model->voxel[i].uncertain = *data++; |
3752 |
|
✗ |
model->voxel[i].groupid = *data++; |
3753 |
|
✗ |
k = model->voxel[i].w * model->voxel[i].h * model->voxel[i].d; |
3754 |
|
✗ |
model->voxel[i].data = (M3D_VOXEL*)M3D_MALLOC(k * sizeof(M3D_VOXEL)); |
3755 |
|
✗ |
if(!model->voxel[i].data) goto memerr; |
3756 |
|
|
memset(model->voxel[i].data, 0xff, k * sizeof(M3D_VOXEL)); |
3757 |
|
✗ |
for(j = 0; data < chunk && j < k;) { |
3758 |
|
✗ |
l = ((*data++) & 0x7F) + 1; |
3759 |
|
✗ |
if(data[-1] & 0x80) { |
3760 |
|
✗ |
data = _m3d_getidx(data, model->vp_s, &mi); |
3761 |
|
✗ |
while(l-- && j < k) model->voxel[i].data[j++] = (M3D_VOXEL)mi; |
3762 |
|
|
} else |
3763 |
|
✗ |
while(l-- && j < k) { |
3764 |
|
✗ |
data = _m3d_getidx(data, model->vp_s, &mi); |
3765 |
|
✗ |
model->voxel[i].data[j++] = (M3D_VOXEL)mi; |
3766 |
|
|
} |
3767 |
|
|
} |
3768 |
|
|
} else |
3769 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'S','H','P','E')) { |
3770 |
|
|
/* mathematical shape */ |
3771 |
|
✗ |
data += sizeof(m3dchunk_t); |
3772 |
|
✗ |
M3D_GETSTR(name); |
3773 |
|
|
M3D_LOG("Mathematical Shape"); |
3774 |
|
|
M3D_LOG(name); |
3775 |
|
✗ |
i = model->numshape++; |
3776 |
|
✗ |
model->shape = (m3dh_t*)M3D_REALLOC(model->shape, model->numshape * sizeof(m3dh_t)); |
3777 |
|
✗ |
if(!model->shape) goto memerr; |
3778 |
|
✗ |
h = &model->shape[i]; |
3779 |
|
✗ |
h->numcmd = 0; |
3780 |
|
✗ |
h->cmd = NULL; |
3781 |
|
✗ |
h->name = name; |
3782 |
|
✗ |
h->group = M3D_UNDEF; |
3783 |
|
✗ |
data = _m3d_getidx(data, model->bi_s, &h->group); |
3784 |
|
✗ |
if(h->group != M3D_UNDEF && h->group >= model->numbone) { |
3785 |
|
|
M3D_LOG("Unknown bone id as shape group in shape"); |
3786 |
|
|
M3D_LOG(name); |
3787 |
|
✗ |
h->group = M3D_UNDEF; |
3788 |
|
✗ |
model->errcode = M3D_ERR_SHPE; |
3789 |
|
|
} |
3790 |
|
✗ |
while(data < chunk) { |
3791 |
|
✗ |
i = h->numcmd++; |
3792 |
|
✗ |
h->cmd = (m3dc_t*)M3D_REALLOC(h->cmd, h->numcmd * sizeof(m3dc_t)); |
3793 |
|
✗ |
if(!h->cmd) goto memerr; |
3794 |
|
✗ |
h->cmd[i].type = *data++; |
3795 |
|
✗ |
if(h->cmd[i].type & 0x80) { |
3796 |
|
✗ |
h->cmd[i].type &= 0x7F; |
3797 |
|
✗ |
h->cmd[i].type |= (*data++ << 7); |
3798 |
|
|
} |
3799 |
|
✗ |
if(h->cmd[i].type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0]))) { |
3800 |
|
|
M3D_LOG("Unknown shape command in"); |
3801 |
|
|
M3D_LOG(h->name); |
3802 |
|
✗ |
model->errcode = M3D_ERR_UNKCMD; |
3803 |
|
✗ |
break; |
3804 |
|
|
} |
3805 |
|
✗ |
cd = &m3d_commandtypes[h->cmd[i].type]; |
3806 |
|
✗ |
h->cmd[i].arg = (uint32_t*)M3D_MALLOC(cd->p * sizeof(uint32_t)); |
3807 |
|
✗ |
if(!h->cmd[i].arg) goto memerr; |
3808 |
|
✗ |
memset(h->cmd[i].arg, 0, cd->p * sizeof(uint32_t)); |
3809 |
|
✗ |
for(k = n = 0, l = cd->p; k < l; k++) |
3810 |
|
✗ |
switch(cd->a[((k - n) % (cd->p - n)) + n]) { |
3811 |
|
✗ |
case m3dcp_mi_t: |
3812 |
|
✗ |
h->cmd[i].arg[k] = M3D_NOTDEFINED; |
3813 |
|
✗ |
M3D_GETSTR(name); |
3814 |
|
|
if(name) { |
3815 |
|
✗ |
for(n = 0; n < model->nummaterial; n++) |
3816 |
|
✗ |
if(!strcmp(name, model->material[n].name)) { |
3817 |
|
✗ |
h->cmd[i].arg[k] = n; |
3818 |
|
✗ |
break; |
3819 |
|
|
} |
3820 |
|
✗ |
if(h->cmd[i].arg[k] == M3D_NOTDEFINED) model->errcode = M3D_ERR_MTRL; |
3821 |
|
|
} |
3822 |
|
|
break; |
3823 |
|
✗ |
case m3dcp_vc_t: |
3824 |
|
✗ |
f = 0.0f; |
3825 |
|
✗ |
switch(model->vc_s) { |
3826 |
|
✗ |
case 1: f = (float)((int8_t)data[0]) / 127; break; |
3827 |
|
✗ |
case 2: f = (float)(*((int16_t*)(data+0))) / 32767; break; |
3828 |
|
✗ |
case 4: f = (float)(*((float*)(data+0))); break; |
3829 |
|
✗ |
case 8: f = (float)(*((double*)(data+0))); break; |
3830 |
|
|
} |
3831 |
|
✗ |
memcpy(&h->cmd[i].arg[k], &f, 4); |
3832 |
|
✗ |
data += model->vc_s; |
3833 |
|
✗ |
break; |
3834 |
|
✗ |
case m3dcp_hi_t: data = _m3d_getidx(data, model->hi_s, &h->cmd[i].arg[k]); break; |
3835 |
|
✗ |
case m3dcp_fi_t: data = _m3d_getidx(data, model->fi_s, &h->cmd[i].arg[k]); break; |
3836 |
|
✗ |
case m3dcp_ti_t: data = _m3d_getidx(data, model->ti_s, &h->cmd[i].arg[k]); break; |
3837 |
|
✗ |
case m3dcp_qi_t: |
3838 |
|
✗ |
case m3dcp_vi_t: data = _m3d_getidx(data, model->vi_s, &h->cmd[i].arg[k]); break; |
3839 |
|
✗ |
case m3dcp_i1_t: data = _m3d_getidx(data, 1, &h->cmd[i].arg[k]); break; |
3840 |
|
✗ |
case m3dcp_i2_t: data = _m3d_getidx(data, 2, &h->cmd[i].arg[k]); break; |
3841 |
|
✗ |
case m3dcp_i4_t: data = _m3d_getidx(data, 4, &h->cmd[i].arg[k]); break; |
3842 |
|
✗ |
case m3dcp_va_t: data = _m3d_getidx(data, 4, &h->cmd[i].arg[k]); |
3843 |
|
✗ |
n = k + 1; l += (h->cmd[i].arg[k] - 1) * (cd->p - k - 1); |
3844 |
|
✗ |
h->cmd[i].arg = (uint32_t*)M3D_REALLOC(h->cmd[i].arg, l * sizeof(uint32_t)); |
3845 |
|
✗ |
if(!h->cmd[i].arg) goto memerr; |
3846 |
|
✗ |
memset(&h->cmd[i].arg[k + 1], 0, (l - k - 1) * sizeof(uint32_t)); |
3847 |
|
|
break; |
3848 |
|
|
} |
3849 |
|
|
} |
3850 |
|
|
} else |
3851 |
|
|
/* annotation label list */ |
3852 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'L','B','L','S')) { |
3853 |
|
✗ |
data += sizeof(m3dchunk_t); |
3854 |
|
✗ |
M3D_GETSTR(name); |
3855 |
|
✗ |
M3D_GETSTR(lang); |
3856 |
|
|
M3D_LOG("Label list"); |
3857 |
|
|
if(name) { M3D_LOG(name); } |
3858 |
|
|
if(lang) { M3D_LOG(lang); } |
3859 |
|
✗ |
if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP; |
3860 |
|
|
k = 0; |
3861 |
|
✗ |
switch(model->ci_s) { |
3862 |
|
✗ |
case 1: k = model->cmap ? model->cmap[data[0]] : 0; data++; break; |
3863 |
|
✗ |
case 2: k = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break; |
3864 |
|
✗ |
case 4: k = *((uint32_t*)data); data += 4; break; |
3865 |
|
|
/* case 8: break; */ |
3866 |
|
|
} |
3867 |
|
✗ |
reclen = model->vi_s + model->si_s; |
3868 |
|
✗ |
i = model->numlabel; model->numlabel += len / reclen; |
3869 |
|
✗ |
model->label = (m3dl_t*)M3D_REALLOC(model->label, model->numlabel * sizeof(m3dl_t)); |
3870 |
|
✗ |
if(!model->label) goto memerr; |
3871 |
|
✗ |
memset(&model->label[i], 0, (model->numlabel - i) * sizeof(m3dl_t)); |
3872 |
|
✗ |
for(; data < chunk && i < model->numlabel; i++) { |
3873 |
|
✗ |
model->label[i].name = name; |
3874 |
|
✗ |
model->label[i].lang = lang; |
3875 |
|
✗ |
model->label[i].color = k; |
3876 |
|
✗ |
data = _m3d_getidx(data, model->vi_s, &model->label[i].vertexid); |
3877 |
|
✗ |
M3D_GETSTR(model->label[i].text); |
3878 |
|
|
} |
3879 |
|
|
} else |
3880 |
|
|
/* action */ |
3881 |
|
✗ |
if(M3D_CHUNKMAGIC(data, 'A','C','T','N')) { |
3882 |
|
|
M3D_LOG("Action"); |
3883 |
|
✗ |
i = model->numaction++; |
3884 |
|
✗ |
model->action = (m3da_t*)M3D_REALLOC(model->action, model->numaction * sizeof(m3da_t)); |
3885 |
|
✗ |
if(!model->action) goto memerr; |
3886 |
|
✗ |
a = &model->action[i]; |
3887 |
|
✗ |
data += sizeof(m3dchunk_t); |
3888 |
|
✗ |
M3D_GETSTR(a->name); |
3889 |
|
|
M3D_LOG(a->name); |
3890 |
|
✗ |
a->numframe = *((uint16_t*)data); data += 2; |
3891 |
|
✗ |
if(a->numframe < 1) { |
3892 |
|
✗ |
model->numaction--; |
3893 |
|
|
} else { |
3894 |
|
✗ |
a->durationmsec = *((uint32_t*)data); data += 4; |
3895 |
|
✗ |
a->frame = (m3dfr_t*)M3D_MALLOC(a->numframe * sizeof(m3dfr_t)); |
3896 |
|
✗ |
if(!a->frame) goto memerr; |
3897 |
|
✗ |
for(i = 0; data < chunk && i < a->numframe; i++) { |
3898 |
|
✗ |
a->frame[i].msec = *((uint32_t*)data); data += 4; |
3899 |
|
✗ |
a->frame[i].numtransform = 0; a->frame[i].transform = NULL; |
3900 |
|
✗ |
data = _m3d_getidx(data, model->fc_s, &a->frame[i].numtransform); |
3901 |
|
✗ |
if(a->frame[i].numtransform > 0) { |
3902 |
|
✗ |
a->frame[i].transform = (m3dtr_t*)M3D_MALLOC(a->frame[i].numtransform * sizeof(m3dtr_t)); |
3903 |
|
✗ |
for(j = 0; j < a->frame[i].numtransform; j++) { |
3904 |
|
✗ |
data = _m3d_getidx(data, model->bi_s, &a->frame[i].transform[j].boneid); |
3905 |
|
✗ |
data = _m3d_getidx(data, model->vi_s, &a->frame[i].transform[j].pos); |
3906 |
|
✗ |
data = _m3d_getidx(data, model->vi_s, &a->frame[i].transform[j].ori); |
3907 |
|
|
} |
3908 |
|
|
} |
3909 |
|
|
} |
3910 |
|
|
} |
3911 |
|
|
} else { |
3912 |
|
✗ |
i = model->numextra++; |
3913 |
|
✗ |
model->extra = (m3dchunk_t**)M3D_REALLOC(model->extra, model->numextra * sizeof(m3dchunk_t*)); |
3914 |
|
✗ |
if(!model->extra) goto memerr; |
3915 |
|
✗ |
model->extra[i] = (m3dchunk_t*)data; |
3916 |
|
|
} |
3917 |
|
|
} |
3918 |
|
|
/* calculate normals, normalize skin weights, create bone/vertex cross-references and calculate transform matrices */ |
3919 |
|
|
#ifdef M3D_ASCII |
3920 |
|
|
postprocess: |
3921 |
|
|
#endif |
3922 |
|
|
if(model) { |
3923 |
|
|
M3D_LOG("Post-process"); |
3924 |
|
|
#ifdef M3D_PROFILING |
3925 |
|
|
gettimeofday(&tv1, NULL); |
3926 |
|
|
tvd.tv_sec = tv1.tv_sec - tv0.tv_sec; |
3927 |
|
|
tvd.tv_usec = tv1.tv_usec - tv0.tv_usec; |
3928 |
|
|
if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; } |
3929 |
|
|
printf(" Parsing chunks %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec); |
3930 |
|
|
#endif |
3931 |
|
|
#ifndef M3D_NOVOXELS |
3932 |
|
✗ |
if(model->numvoxel && model->voxel) { |
3933 |
|
|
M3D_LOG("Converting voxels into vertices and mesh"); |
3934 |
|
|
/* add normals */ |
3935 |
|
✗ |
enorm = model->numvertex; model->numvertex += 6; |
3936 |
|
✗ |
model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t)); |
3937 |
|
✗ |
if(!model->vertex) goto memerr; |
3938 |
|
✗ |
memset(&model->vertex[enorm], 0, 6 * sizeof(m3dv_t)); |
3939 |
|
✗ |
for(l = 0; l < 6; l++) |
3940 |
|
✗ |
model->vertex[enorm+l].skinid = M3D_UNDEF; |
3941 |
|
✗ |
model->vertex[enorm+0].y = (M3D_FLOAT)-1.0; |
3942 |
|
✗ |
model->vertex[enorm+1].z = (M3D_FLOAT)-1.0; |
3943 |
|
✗ |
model->vertex[enorm+2].x = (M3D_FLOAT)-1.0; |
3944 |
|
✗ |
model->vertex[enorm+3].y = (M3D_FLOAT)1.0; |
3945 |
|
✗ |
model->vertex[enorm+4].z = (M3D_FLOAT)1.0; |
3946 |
|
✗ |
model->vertex[enorm+5].x = (M3D_FLOAT)1.0; |
3947 |
|
|
/* this is a fast, not so memory efficient version, only basic face culling used */ |
3948 |
|
|
min_x = min_y = min_z = 2147483647L; |
3949 |
|
|
max_x = max_y = max_z = -2147483647L; |
3950 |
|
✗ |
for(i = 0; i < model->numvoxel; i++) { |
3951 |
|
✗ |
if(model->voxel[i].x + (int32_t)model->voxel[i].w > max_x) max_x = model->voxel[i].x + (int32_t)model->voxel[i].w; |
3952 |
|
|
if(model->voxel[i].x < min_x) min_x = model->voxel[i].x; |
3953 |
|
✗ |
if(model->voxel[i].y + (int32_t)model->voxel[i].h > max_y) max_y = model->voxel[i].y + (int32_t)model->voxel[i].h; |
3954 |
|
|
if(model->voxel[i].y < min_y) min_y = model->voxel[i].y; |
3955 |
|
✗ |
if(model->voxel[i].z + (int32_t)model->voxel[i].d > max_z) max_z = model->voxel[i].z + (int32_t)model->voxel[i].d; |
3956 |
|
|
if(model->voxel[i].z < min_z) min_z = model->voxel[i].z; |
3957 |
|
|
} |
3958 |
|
✗ |
i = (-min_x > max_x ? -min_x : max_x); |
3959 |
|
✗ |
j = (-min_y > max_y ? -min_y : max_y); |
3960 |
|
✗ |
k = (-min_z > max_z ? -min_z : max_z); |
3961 |
|
|
if(j > i) i = j; |
3962 |
|
|
if(k > i) i = k; |
3963 |
|
|
if(i <= 1) i = 1; |
3964 |
|
✗ |
w = (M3D_FLOAT)1.0 / (M3D_FLOAT)i; |
3965 |
|
✗ |
if(i >= 254) model->vc_s = 2; |
3966 |
|
✗ |
if(i >= 65534) model->vc_s = 4; |
3967 |
|
✗ |
for(i = 0; i < model->numvoxel; i++) { |
3968 |
|
✗ |
sx = model->voxel[i].w; sz = model->voxel[i].d; sy = model->voxel[i].h; |
3969 |
|
✗ |
for(y = 0, j = 0; y < sy; y++) |
3970 |
|
✗ |
for(z = 0; z < sz; z++) |
3971 |
|
✗ |
for(x = 0; x < sx; x++, j++) |
3972 |
|
✗ |
if(model->voxel[i].data[j] < model->numvoxtype) { |
3973 |
|
|
k = 0; |
3974 |
|
|
/* 16__32 ____ |
3975 |
|
|
* /| /| /|2 /| |
3976 |
|
|
*64_128 | /_8_/ 32 |
3977 |
|
|
* | 1_|_2 |4|_|_| |
3978 |
|
|
* |/ |/ |/ 1|/ |
3979 |
|
|
* 4___8 |16_| */ |
3980 |
|
|
k = n = am = 0; |
3981 |
|
✗ |
if(!y || model->voxel[i].data[j - sx*sz] >= model->numvoxtype) { n++; am |= 1; k |= 1|2|4|8; } |
3982 |
|
✗ |
if(!z || model->voxel[i].data[j - sx] >= model->numvoxtype) { n++; am |= 2; k |= 1|2|16|32; } |
3983 |
|
✗ |
if(!x || model->voxel[i].data[j - 1] >= model->numvoxtype) { n++; am |= 4; k |= 1|4|16|64; } |
3984 |
|
✗ |
if(y == sy-1 || model->voxel[i].data[j + sx*sz] >= model->numvoxtype) { n++; am |= 8; k |= 16|32|64|128; } |
3985 |
|
✗ |
if(z == sz-1 || model->voxel[i].data[j + sx] >= model->numvoxtype) { n++; am |= 16; k |= 4|8|64|128; } |
3986 |
|
✗ |
if(x == sx-1 || model->voxel[i].data[j + 1] >= model->numvoxtype) { n++; am |= 32; k |= 2|8|32|128; } |
3987 |
|
✗ |
if(k) { |
3988 |
|
|
memset(edge, 255, sizeof(edge)); |
3989 |
|
✗ |
for(l = 0, len = 1, reclen = model->numvertex; l < 8; l++, len <<= 1) |
3990 |
|
✗ |
if(k & len) edge[l] = model->numvertex++; |
3991 |
|
✗ |
model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t)); |
3992 |
|
✗ |
if(!model->vertex) goto memerr; |
3993 |
|
✗ |
memset(&model->vertex[reclen], 0, (model->numvertex-reclen) * sizeof(m3dv_t)); |
3994 |
|
✗ |
for(l = reclen; l < model->numvertex; l++) { |
3995 |
|
✗ |
model->vertex[l].skinid = model->voxtype[model->voxel[i].data[j]].skinid; |
3996 |
|
✗ |
model->vertex[l].color = model->voxtype[model->voxel[i].data[j]].color; |
3997 |
|
|
} |
3998 |
|
|
l = reclen; |
3999 |
|
✗ |
if(k & 1) { |
4000 |
|
✗ |
model->vertex[l].x = (model->voxel[i].x + x) * w; |
4001 |
|
✗ |
model->vertex[l].y = (model->voxel[i].y + y) * w; |
4002 |
|
✗ |
model->vertex[l].z = (model->voxel[i].z + z) * w; |
4003 |
|
✗ |
l++; |
4004 |
|
|
} |
4005 |
|
✗ |
if(k & 2) { |
4006 |
|
✗ |
model->vertex[l].x = (model->voxel[i].x + x + 1) * w; |
4007 |
|
✗ |
model->vertex[l].y = (model->voxel[i].y + y) * w; |
4008 |
|
✗ |
model->vertex[l].z = (model->voxel[i].z + z) * w; |
4009 |
|
✗ |
l++; |
4010 |
|
|
} |
4011 |
|
✗ |
if(k & 4) { |
4012 |
|
✗ |
model->vertex[l].x = (model->voxel[i].x + x) * w; |
4013 |
|
✗ |
model->vertex[l].y = (model->voxel[i].y + y) * w; |
4014 |
|
✗ |
model->vertex[l].z = (model->voxel[i].z + z + 1) * w; |
4015 |
|
✗ |
l++; |
4016 |
|
|
} |
4017 |
|
✗ |
if(k & 8) { |
4018 |
|
✗ |
model->vertex[l].x = (model->voxel[i].x + x + 1) * w; |
4019 |
|
✗ |
model->vertex[l].y = (model->voxel[i].y + y) * w; |
4020 |
|
✗ |
model->vertex[l].z = (model->voxel[i].z + z + 1) * w; |
4021 |
|
✗ |
l++; |
4022 |
|
|
} |
4023 |
|
✗ |
if(k & 16) { |
4024 |
|
✗ |
model->vertex[l].x = (model->voxel[i].x + x) * w; |
4025 |
|
✗ |
model->vertex[l].y = (model->voxel[i].y + y + 1) * w; |
4026 |
|
✗ |
model->vertex[l].z = (model->voxel[i].z + z) * w; |
4027 |
|
✗ |
l++; |
4028 |
|
|
} |
4029 |
|
✗ |
if(k & 32) { |
4030 |
|
✗ |
model->vertex[l].x = (model->voxel[i].x + x + 1) * w; |
4031 |
|
✗ |
model->vertex[l].y = (model->voxel[i].y + y + 1) * w; |
4032 |
|
✗ |
model->vertex[l].z = (model->voxel[i].z + z) * w; |
4033 |
|
✗ |
l++; |
4034 |
|
|
} |
4035 |
|
✗ |
if(k & 64) { |
4036 |
|
✗ |
model->vertex[l].x = (model->voxel[i].x + x) * w; |
4037 |
|
✗ |
model->vertex[l].y = (model->voxel[i].y + y + 1) * w; |
4038 |
|
✗ |
model->vertex[l].z = (model->voxel[i].z + z + 1) * w; |
4039 |
|
✗ |
l++; |
4040 |
|
|
} |
4041 |
|
✗ |
if(k & 128) { |
4042 |
|
✗ |
model->vertex[l].x = (model->voxel[i].x + x + 1) * w; |
4043 |
|
✗ |
model->vertex[l].y = (model->voxel[i].y + y + 1) * w; |
4044 |
|
✗ |
model->vertex[l].z = (model->voxel[i].z + z + 1) * w; |
4045 |
|
|
l++; |
4046 |
|
|
} |
4047 |
|
✗ |
n <<= 1; |
4048 |
|
✗ |
l = model->numface; model->numface += n; |
4049 |
|
✗ |
model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t)); |
4050 |
|
✗ |
if(!model->face) goto memerr; |
4051 |
|
✗ |
memset(&model->face[l], 255, n * sizeof(m3df_t)); |
4052 |
|
✗ |
for(reclen = l; reclen < model->numface; reclen++) |
4053 |
|
✗ |
model->face[reclen].materialid = model->voxtype[model->voxel[i].data[j]].materialid; |
4054 |
|
✗ |
if(am & 1) { /* bottom */ |
4055 |
|
✗ |
model->face[l].vertex[0] = edge[0]; model->face[l].vertex[1] = edge[1]; model->face[l].vertex[2] = edge[2]; |
4056 |
|
✗ |
model->face[l+1].vertex[0] = edge[2]; model->face[l+1].vertex[1] = edge[1]; model->face[l+1].vertex[2] = edge[3]; |
4057 |
|
✗ |
model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = |
4058 |
|
✗ |
model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm; |
4059 |
|
✗ |
l += 2; |
4060 |
|
|
} |
4061 |
|
✗ |
if(am & 2) { /* north */ |
4062 |
|
✗ |
model->face[l].vertex[0] = edge[0]; model->face[l].vertex[1] = edge[4]; model->face[l].vertex[2] = edge[1]; |
4063 |
|
✗ |
model->face[l+1].vertex[0] = edge[1]; model->face[l+1].vertex[1] = edge[4]; model->face[l+1].vertex[2] = edge[5]; |
4064 |
|
✗ |
model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = |
4065 |
|
✗ |
model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+1; |
4066 |
|
✗ |
l += 2; |
4067 |
|
|
} |
4068 |
|
✗ |
if(am & 4) { /* west */ |
4069 |
|
✗ |
model->face[l].vertex[0] = edge[0]; model->face[l].vertex[1] = edge[2]; model->face[l].vertex[2] = edge[4]; |
4070 |
|
✗ |
model->face[l+1].vertex[0] = edge[2]; model->face[l+1].vertex[1] = edge[6]; model->face[l+1].vertex[2] = edge[4]; |
4071 |
|
✗ |
model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = |
4072 |
|
✗ |
model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+2; |
4073 |
|
✗ |
l += 2; |
4074 |
|
|
} |
4075 |
|
✗ |
if(am & 8) { /* top */ |
4076 |
|
✗ |
model->face[l].vertex[0] = edge[4]; model->face[l].vertex[1] = edge[6]; model->face[l].vertex[2] = edge[5]; |
4077 |
|
✗ |
model->face[l+1].vertex[0] = edge[5]; model->face[l+1].vertex[1] = edge[6]; model->face[l+1].vertex[2] = edge[7]; |
4078 |
|
✗ |
model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = |
4079 |
|
✗ |
model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+3; |
4080 |
|
✗ |
l += 2; |
4081 |
|
|
} |
4082 |
|
✗ |
if(am & 16) { /* south */ |
4083 |
|
✗ |
model->face[l].vertex[0] = edge[2]; model->face[l].vertex[1] = edge[7]; model->face[l].vertex[2] = edge[6]; |
4084 |
|
✗ |
model->face[l+1].vertex[0] = edge[7]; model->face[l+1].vertex[1] = edge[2]; model->face[l+1].vertex[2] = edge[3]; |
4085 |
|
✗ |
model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = |
4086 |
|
✗ |
model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+4; |
4087 |
|
✗ |
l += 2; |
4088 |
|
|
} |
4089 |
|
✗ |
if(am & 32) { /* east */ |
4090 |
|
✗ |
model->face[l].vertex[0] = edge[1]; model->face[l].vertex[1] = edge[5]; model->face[l].vertex[2] = edge[7]; |
4091 |
|
✗ |
model->face[l+1].vertex[0] = edge[1]; model->face[l+1].vertex[1] = edge[7]; model->face[l+1].vertex[2] = edge[3]; |
4092 |
|
✗ |
model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] = |
4093 |
|
✗ |
model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+5; |
4094 |
|
|
l += 2; |
4095 |
|
|
} |
4096 |
|
|
} |
4097 |
|
|
} |
4098 |
|
|
} |
4099 |
|
|
} |
4100 |
|
|
#endif |
4101 |
|
|
#ifndef M3D_NONORMALS |
4102 |
|
✗ |
if(model->numface && model->face && neednorm) { |
4103 |
|
|
/* if they are missing, calculate triangle normals into a temporary buffer */ |
4104 |
|
✗ |
norm = (m3dv_t*)M3D_MALLOC(model->numface * sizeof(m3dv_t)); |
4105 |
|
✗ |
if(!norm) goto memerr; |
4106 |
|
✗ |
for(i = 0, n = model->numvertex; i < model->numface; i++) |
4107 |
|
✗ |
if(model->face[i].normal[0] == M3D_UNDEF) { |
4108 |
|
✗ |
v0 = &model->vertex[model->face[i].vertex[0]]; |
4109 |
|
✗ |
v1 = &model->vertex[model->face[i].vertex[1]]; |
4110 |
|
✗ |
v2 = &model->vertex[model->face[i].vertex[2]]; |
4111 |
|
✗ |
va.x = v1->x - v0->x; va.y = v1->y - v0->y; va.z = v1->z - v0->z; |
4112 |
|
✗ |
vb.x = v2->x - v0->x; vb.y = v2->y - v0->y; vb.z = v2->z - v0->z; |
4113 |
|
✗ |
v0 = &norm[i]; |
4114 |
|
✗ |
v0->x = (va.y * vb.z) - (va.z * vb.y); |
4115 |
|
✗ |
v0->y = (va.z * vb.x) - (va.x * vb.z); |
4116 |
|
✗ |
v0->z = (va.x * vb.y) - (va.y * vb.x); |
4117 |
|
✗ |
w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z)); |
4118 |
|
✗ |
v0->x *= w; v0->y *= w; v0->z *= w; |
4119 |
|
✗ |
model->face[i].normal[0] = model->face[i].vertex[0] + n; |
4120 |
|
✗ |
model->face[i].normal[1] = model->face[i].vertex[1] + n; |
4121 |
|
✗ |
model->face[i].normal[2] = model->face[i].vertex[2] + n; |
4122 |
|
|
} |
4123 |
|
|
/* this is the fast way, we don't care if a normal is repeated in model->vertex */ |
4124 |
|
|
M3D_LOG("Generating normals"); |
4125 |
|
✗ |
model->flags |= M3D_FLG_GENNORM; |
4126 |
|
✗ |
model->numvertex <<= 1; |
4127 |
|
✗ |
model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t)); |
4128 |
|
✗ |
if(!model->vertex) goto memerr; |
4129 |
|
✗ |
memset(&model->vertex[n], 0, n * sizeof(m3dv_t)); |
4130 |
|
✗ |
for(i = 0; i < model->numface; i++) |
4131 |
|
✗ |
for(j = 0; j < 3; j++) { |
4132 |
|
✗ |
v0 = &model->vertex[model->face[i].vertex[j] + n]; |
4133 |
|
✗ |
v0->x += norm[i].x; |
4134 |
|
✗ |
v0->y += norm[i].y; |
4135 |
|
✗ |
v0->z += norm[i].z; |
4136 |
|
|
} |
4137 |
|
|
/* for each vertex, take the average of the temporary normals and use that */ |
4138 |
|
✗ |
for(i = 0, v0 = &model->vertex[n]; i < n; i++, v0++) { |
4139 |
|
✗ |
w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z)); |
4140 |
|
✗ |
v0->x *= w; v0->y *= w; v0->z *= w; |
4141 |
|
✗ |
v0->skinid = M3D_UNDEF; |
4142 |
|
|
} |
4143 |
|
✗ |
M3D_FREE(norm); |
4144 |
|
|
} |
4145 |
|
|
#endif |
4146 |
|
✗ |
if(model->numbone && model->bone && model->numskin && model->skin && model->numvertex && model->vertex) { |
4147 |
|
|
#ifndef M3D_NOWEIGHTS |
4148 |
|
|
M3D_LOG("Generating weight cross-reference"); |
4149 |
|
✗ |
for(i = 0; i < model->numvertex; i++) { |
4150 |
|
✗ |
if(model->vertex[i].skinid < model->numskin) { |
4151 |
|
✗ |
sk = &model->skin[model->vertex[i].skinid]; |
4152 |
|
|
w = (M3D_FLOAT)0.0; |
4153 |
|
✗ |
for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++) |
4154 |
|
✗ |
w += sk->weight[j]; |
4155 |
|
✗ |
for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++) { |
4156 |
|
✗ |
sk->weight[j] /= w; |
4157 |
|
✗ |
b = &model->bone[sk->boneid[j]]; |
4158 |
|
✗ |
k = b->numweight++; |
4159 |
|
✗ |
b->weight = (m3dw_t*)M3D_REALLOC(b->weight, b->numweight * sizeof(m3da_t)); |
4160 |
|
✗ |
if(!b->weight) goto memerr; |
4161 |
|
✗ |
b->weight[k].vertexid = i; |
4162 |
|
✗ |
b->weight[k].weight = sk->weight[j]; |
4163 |
|
|
} |
4164 |
|
|
} |
4165 |
|
|
} |
4166 |
|
|
#endif |
4167 |
|
|
#ifndef M3D_NOANIMATION |
4168 |
|
|
M3D_LOG("Calculating bone transformation matrices"); |
4169 |
|
✗ |
for(i = 0; i < model->numbone; i++) { |
4170 |
|
✗ |
b = &model->bone[i]; |
4171 |
|
✗ |
if(model->bone[i].parent == M3D_UNDEF) { |
4172 |
|
✗ |
_m3d_mat((M3D_FLOAT*)&b->mat4, &model->vertex[b->pos], &model->vertex[b->ori]); |
4173 |
|
|
} else { |
4174 |
|
✗ |
_m3d_mat((M3D_FLOAT*)&r, &model->vertex[b->pos], &model->vertex[b->ori]); |
4175 |
|
✗ |
_m3d_mul((M3D_FLOAT*)&b->mat4, (M3D_FLOAT*)&model->bone[b->parent].mat4, (M3D_FLOAT*)&r); |
4176 |
|
|
} |
4177 |
|
|
} |
4178 |
|
✗ |
for(i = 0; i < model->numbone; i++) |
4179 |
|
✗ |
_m3d_inv((M3D_FLOAT*)&model->bone[i].mat4); |
4180 |
|
|
#endif |
4181 |
|
|
} |
4182 |
|
|
#ifdef M3D_PROFILING |
4183 |
|
|
gettimeofday(&tv0, NULL); |
4184 |
|
|
tvd.tv_sec = tv0.tv_sec - tv1.tv_sec; |
4185 |
|
|
tvd.tv_usec = tv0.tv_usec - tv1.tv_usec; |
4186 |
|
|
if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; } |
4187 |
|
|
printf(" Post-process %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec); |
4188 |
|
|
#endif |
4189 |
|
|
} |
4190 |
|
|
return model; |
4191 |
|
|
} |
4192 |
|
|
|
4193 |
|
|
/** |
4194 |
|
|
* Calculates skeletons for animation frames, returns a working copy (should be freed after use) |
4195 |
|
|
*/ |
4196 |
|
✗ |
m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t *skeleton) |
4197 |
|
|
{ |
4198 |
|
|
unsigned int i; |
4199 |
|
|
M3D_INDEX s = frameid; |
4200 |
|
|
m3dfr_t *fr; |
4201 |
|
|
|
4202 |
|
✗ |
if(!model || !model->numbone || !model->bone || (actionid != M3D_UNDEF && (!model->action || |
4203 |
|
✗ |
actionid >= model->numaction || frameid >= model->action[actionid].numframe))) { |
4204 |
|
✗ |
model->errcode = M3D_ERR_UNKFRAME; |
4205 |
|
✗ |
return skeleton; |
4206 |
|
|
} |
4207 |
|
✗ |
model->errcode = M3D_SUCCESS; |
4208 |
|
✗ |
if(!skeleton) { |
4209 |
|
✗ |
skeleton = (m3dtr_t*)M3D_MALLOC(model->numbone * sizeof(m3dtr_t)); |
4210 |
|
✗ |
if(!skeleton) { |
4211 |
|
✗ |
model->errcode = M3D_ERR_ALLOC; |
4212 |
|
✗ |
return NULL; |
4213 |
|
|
} |
4214 |
|
✗ |
goto gen; |
4215 |
|
|
} |
4216 |
|
✗ |
if(actionid == M3D_UNDEF || !frameid) { |
4217 |
|
✗ |
gen: s = 0; |
4218 |
|
✗ |
for(i = 0; i < model->numbone; i++) { |
4219 |
|
✗ |
skeleton[i].boneid = i; |
4220 |
|
✗ |
skeleton[i].pos = model->bone[i].pos; |
4221 |
|
✗ |
skeleton[i].ori = model->bone[i].ori; |
4222 |
|
|
} |
4223 |
|
|
} |
4224 |
|
✗ |
if(actionid < model->numaction && (frameid || !model->action[actionid].frame[0].msec)) { |
4225 |
|
✗ |
for(; s <= frameid; s++) { |
4226 |
|
✗ |
fr = &model->action[actionid].frame[s]; |
4227 |
|
✗ |
for(i = 0; i < fr->numtransform; i++) { |
4228 |
|
✗ |
skeleton[fr->transform[i].boneid].pos = fr->transform[i].pos; |
4229 |
|
✗ |
skeleton[fr->transform[i].boneid].ori = fr->transform[i].ori; |
4230 |
|
|
} |
4231 |
|
|
} |
4232 |
|
|
} |
4233 |
|
|
return skeleton; |
4234 |
|
|
} |
4235 |
|
|
|
4236 |
|
|
#ifndef M3D_NOANIMATION |
4237 |
|
|
/** |
4238 |
|
|
* Returns interpolated animation-pose, a working copy (should be freed after use) |
4239 |
|
|
*/ |
4240 |
|
✗ |
m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec) |
4241 |
|
|
{ |
4242 |
|
|
unsigned int i, j, l; |
4243 |
|
|
M3D_FLOAT r[16], t, c, d, s; |
4244 |
|
|
m3db_t *ret; |
4245 |
|
|
m3dv_t *v, *p, *f; |
4246 |
|
|
m3dtr_t *tmp; |
4247 |
|
|
m3dfr_t *fr; |
4248 |
|
|
|
4249 |
|
✗ |
if(!model || !model->numbone || !model->bone) { |
4250 |
|
✗ |
model->errcode = M3D_ERR_UNKFRAME; |
4251 |
|
✗ |
return NULL; |
4252 |
|
|
} |
4253 |
|
✗ |
ret = (m3db_t*)M3D_MALLOC(model->numbone * sizeof(m3db_t)); |
4254 |
|
✗ |
if(!ret) { |
4255 |
|
✗ |
model->errcode = M3D_ERR_ALLOC; |
4256 |
|
✗ |
return NULL; |
4257 |
|
|
} |
4258 |
|
|
memcpy(ret, model->bone, model->numbone * sizeof(m3db_t)); |
4259 |
|
✗ |
for(i = 0; i < model->numbone; i++) |
4260 |
|
✗ |
_m3d_inv((M3D_FLOAT*)&ret[i].mat4); |
4261 |
|
✗ |
if(!model->action || actionid >= model->numaction) { |
4262 |
|
✗ |
model->errcode = M3D_ERR_UNKFRAME; |
4263 |
|
✗ |
return ret; |
4264 |
|
|
} |
4265 |
|
✗ |
msec %= model->action[actionid].durationmsec; |
4266 |
|
✗ |
model->errcode = M3D_SUCCESS; |
4267 |
|
|
fr = &model->action[actionid].frame[0]; |
4268 |
|
✗ |
for(j = l = 0; j < model->action[actionid].numframe && model->action[actionid].frame[j].msec <= msec; j++) { |
4269 |
|
|
fr = &model->action[actionid].frame[j]; |
4270 |
|
|
l = fr->msec; |
4271 |
|
✗ |
for(i = 0; i < fr->numtransform; i++) { |
4272 |
|
✗ |
ret[fr->transform[i].boneid].pos = fr->transform[i].pos; |
4273 |
|
✗ |
ret[fr->transform[i].boneid].ori = fr->transform[i].ori; |
4274 |
|
|
} |
4275 |
|
|
} |
4276 |
|
✗ |
if(l != msec) { |
4277 |
|
✗ |
model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, (model->numvertex + 2 * model->numbone) * sizeof(m3dv_t)); |
4278 |
|
✗ |
if(!model->vertex) { |
4279 |
|
✗ |
free(ret); |
4280 |
|
✗ |
model->errcode = M3D_ERR_ALLOC; |
4281 |
|
✗ |
return NULL; |
4282 |
|
|
} |
4283 |
|
✗ |
tmp = (m3dtr_t*)M3D_MALLOC(model->numbone * sizeof(m3dtr_t)); |
4284 |
|
✗ |
if(tmp) { |
4285 |
|
✗ |
for(i = 0; i < model->numbone; i++) { |
4286 |
|
✗ |
tmp[i].pos = ret[i].pos; |
4287 |
|
✗ |
tmp[i].ori = ret[i].ori; |
4288 |
|
|
} |
4289 |
|
✗ |
fr = &model->action[actionid].frame[j % model->action[actionid].numframe]; |
4290 |
|
✗ |
t = l >= fr->msec ? (M3D_FLOAT)1.0 : (M3D_FLOAT)(msec - l) / (M3D_FLOAT)(fr->msec - l); |
4291 |
|
✗ |
for(i = 0; i < fr->numtransform; i++) { |
4292 |
|
✗ |
tmp[fr->transform[i].boneid].pos = fr->transform[i].pos; |
4293 |
|
✗ |
tmp[fr->transform[i].boneid].ori = fr->transform[i].ori; |
4294 |
|
|
} |
4295 |
|
✗ |
for(i = 0, j = model->numvertex; i < model->numbone; i++) { |
4296 |
|
|
/* interpolation of position */ |
4297 |
|
✗ |
if(ret[i].pos != tmp[i].pos) { |
4298 |
|
✗ |
p = &model->vertex[ret[i].pos]; |
4299 |
|
✗ |
f = &model->vertex[tmp[i].pos]; |
4300 |
|
✗ |
v = &model->vertex[j]; |
4301 |
|
✗ |
v->x = p->x + t * (f->x - p->x); |
4302 |
|
✗ |
v->y = p->y + t * (f->y - p->y); |
4303 |
|
✗ |
v->z = p->z + t * (f->z - p->z); |
4304 |
|
✗ |
ret[i].pos = j++; |
4305 |
|
|
} |
4306 |
|
|
/* interpolation of orientation */ |
4307 |
|
✗ |
if(ret[i].ori != tmp[i].ori) { |
4308 |
|
✗ |
p = &model->vertex[ret[i].ori]; |
4309 |
|
✗ |
f = &model->vertex[tmp[i].ori]; |
4310 |
|
✗ |
v = &model->vertex[j]; |
4311 |
|
✗ |
d = p->w * f->w + p->x * f->x + p->y * f->y + p->z * f->z; |
4312 |
|
✗ |
if(d < 0) { d = -d; s = (M3D_FLOAT)-1.0; } else s = (M3D_FLOAT)1.0; |
4313 |
|
|
#if 0 |
4314 |
|
|
/* don't use SLERP, requires two more variables, libm linkage and it is slow (but nice) */ |
4315 |
|
|
a = (M3D_FLOAT)1.0 - t; b = t; |
4316 |
|
|
if(d < (M3D_FLOAT)0.999999) { c = acosf(d); b = 1 / sinf(c); a = sinf(a * c) * b; b *= sinf(t * c) * s; } |
4317 |
|
|
v->x = p->x * a + f->x * b; |
4318 |
|
|
v->y = p->y * a + f->y * b; |
4319 |
|
|
v->z = p->z * a + f->z * b; |
4320 |
|
|
v->w = p->w * a + f->w * b; |
4321 |
|
|
#else |
4322 |
|
|
/* approximated NLERP, original approximation by Arseny Kapoulkine, heavily optimized by me */ |
4323 |
|
✗ |
c = t - (M3D_FLOAT)0.5; t += t * c * (t - (M3D_FLOAT)1.0) * (((M3D_FLOAT)1.0904 + d * ((M3D_FLOAT)-3.2452 + |
4324 |
|
✗ |
d * ((M3D_FLOAT)3.55645 - d * (M3D_FLOAT)1.43519))) * c * c + ((M3D_FLOAT)0.848013 + d * |
4325 |
|
✗ |
((M3D_FLOAT)-1.06021 + d * (M3D_FLOAT)0.215638))); |
4326 |
|
✗ |
v->x = p->x + t * (s * f->x - p->x); |
4327 |
|
✗ |
v->y = p->y + t * (s * f->y - p->y); |
4328 |
|
✗ |
v->z = p->z + t * (s * f->z - p->z); |
4329 |
|
✗ |
v->w = p->w + t * (s * f->w - p->w); |
4330 |
|
✗ |
d = _m3d_rsq(v->w * v->w + v->x * v->x + v->y * v->y + v->z * v->z); |
4331 |
|
✗ |
v->x *= d; v->y *= d; v->z *= d; v->w *= d; |
4332 |
|
|
#endif |
4333 |
|
✗ |
ret[i].ori = j++; |
4334 |
|
|
} |
4335 |
|
|
} |
4336 |
|
✗ |
M3D_FREE(tmp); |
4337 |
|
|
} |
4338 |
|
|
} |
4339 |
|
✗ |
for(i = 0; i < model->numbone; i++) { |
4340 |
|
✗ |
if(ret[i].parent == M3D_UNDEF) { |
4341 |
|
✗ |
_m3d_mat((M3D_FLOAT*)&ret[i].mat4, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]); |
4342 |
|
|
} else { |
4343 |
|
✗ |
_m3d_mat((M3D_FLOAT*)&r, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]); |
4344 |
|
✗ |
_m3d_mul((M3D_FLOAT*)&ret[i].mat4, (M3D_FLOAT*)&ret[ret[i].parent].mat4, (M3D_FLOAT*)&r); |
4345 |
|
|
} |
4346 |
|
|
} |
4347 |
|
|
return ret; |
4348 |
|
|
} |
4349 |
|
|
|
4350 |
|
|
#endif /* M3D_NOANIMATION */ |
4351 |
|
|
|
4352 |
|
|
#endif /* M3D_IMPLEMENTATION */ |
4353 |
|
|
|
4354 |
|
|
#if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_EXPORTER)) |
4355 |
|
|
/** |
4356 |
|
|
* Free the in-memory model |
4357 |
|
|
*/ |
4358 |
|
✗ |
void m3d_free(m3d_t *model) |
4359 |
|
|
{ |
4360 |
|
|
unsigned int i, j; |
4361 |
|
|
|
4362 |
|
✗ |
if(!model) return; |
4363 |
|
|
#ifdef M3D_ASCII |
4364 |
|
|
/* if model imported from ASCII, we have to free all strings as well */ |
4365 |
|
|
if(model->flags & M3D_FLG_FREESTR) { |
4366 |
|
|
if(model->name) M3D_FREE(model->name); |
4367 |
|
|
if(model->license) M3D_FREE(model->license); |
4368 |
|
|
if(model->author) M3D_FREE(model->author); |
4369 |
|
|
if(model->desc) M3D_FREE(model->desc); |
4370 |
|
|
if(model->bone) |
4371 |
|
|
for(i = 0; i < model->numbone; i++) |
4372 |
|
|
if(model->bone[i].name) |
4373 |
|
|
M3D_FREE(model->bone[i].name); |
4374 |
|
|
if(model->shape) |
4375 |
|
|
for(i = 0; i < model->numshape; i++) |
4376 |
|
|
if(model->shape[i].name) |
4377 |
|
|
M3D_FREE(model->shape[i].name); |
4378 |
|
|
if(model->numvoxtype) |
4379 |
|
|
for(i = 0; i < model->numvoxtype; i++) { |
4380 |
|
|
if(model->voxtype[i].name) |
4381 |
|
|
M3D_FREE(model->voxtype[i].name); |
4382 |
|
|
for(j = 0; j < model->voxtype[i].numitem; j++) |
4383 |
|
|
if(model->voxtype[i].item[j].name) |
4384 |
|
|
M3D_FREE(model->voxtype[i].item[j].name); |
4385 |
|
|
} |
4386 |
|
|
if(model->numvoxel) |
4387 |
|
|
for(i = 0; i < model->numvoxel; i++) |
4388 |
|
|
if(model->voxel[i].name) |
4389 |
|
|
M3D_FREE(model->voxel[i].name); |
4390 |
|
|
if(model->material) |
4391 |
|
|
for(i = 0; i < model->nummaterial; i++) |
4392 |
|
|
if(model->material[i].name) |
4393 |
|
|
M3D_FREE(model->material[i].name); |
4394 |
|
|
if(model->action) |
4395 |
|
|
for(i = 0; i < model->numaction; i++) |
4396 |
|
|
if(model->action[i].name) |
4397 |
|
|
M3D_FREE(model->action[i].name); |
4398 |
|
|
if(model->texture) |
4399 |
|
|
for(i = 0; i < model->numtexture; i++) |
4400 |
|
|
if(model->texture[i].name) |
4401 |
|
|
M3D_FREE(model->texture[i].name); |
4402 |
|
|
if(model->inlined) |
4403 |
|
|
for(i = 0; i < model->numinlined; i++) { |
4404 |
|
|
if(model->inlined[i].name) |
4405 |
|
|
M3D_FREE(model->inlined[i].name); |
4406 |
|
|
if(model->inlined[i].data) |
4407 |
|
|
M3D_FREE(model->inlined[i].data); |
4408 |
|
|
} |
4409 |
|
|
if(model->extra) |
4410 |
|
|
for(i = 0; i < model->numextra; i++) |
4411 |
|
|
if(model->extra[i]) |
4412 |
|
|
M3D_FREE(model->extra[i]); |
4413 |
|
|
if(model->label) |
4414 |
|
|
for(i = 0; i < model->numlabel; i++) { |
4415 |
|
|
if(model->label[i].name) { |
4416 |
|
|
for(j = i + 1; j < model->numlabel; j++) |
4417 |
|
|
if(model->label[j].name == model->label[i].name) |
4418 |
|
|
model->label[j].name = NULL; |
4419 |
|
|
M3D_FREE(model->label[i].name); |
4420 |
|
|
} |
4421 |
|
|
if(model->label[i].lang) { |
4422 |
|
|
for(j = i + 1; j < model->numlabel; j++) |
4423 |
|
|
if(model->label[j].lang == model->label[i].lang) |
4424 |
|
|
model->label[j].lang = NULL; |
4425 |
|
|
M3D_FREE(model->label[i].lang); |
4426 |
|
|
} |
4427 |
|
|
if(model->label[i].text) |
4428 |
|
|
M3D_FREE(model->label[i].text); |
4429 |
|
|
} |
4430 |
|
|
if(model->preview.data) |
4431 |
|
|
M3D_FREE(model->preview.data); |
4432 |
|
|
} |
4433 |
|
|
#endif |
4434 |
|
✗ |
if(model->flags & M3D_FLG_FREERAW) M3D_FREE(model->raw); |
4435 |
|
|
|
4436 |
|
✗ |
if(model->tmap) M3D_FREE(model->tmap); |
4437 |
|
✗ |
if(model->bone) { |
4438 |
|
✗ |
for(i = 0; i < model->numbone; i++) |
4439 |
|
✗ |
if(model->bone[i].weight) |
4440 |
|
✗ |
M3D_FREE(model->bone[i].weight); |
4441 |
|
✗ |
M3D_FREE(model->bone); |
4442 |
|
|
} |
4443 |
|
✗ |
if(model->skin) M3D_FREE(model->skin); |
4444 |
|
✗ |
if(model->vertex) M3D_FREE(model->vertex); |
4445 |
|
✗ |
if(model->face) M3D_FREE(model->face); |
4446 |
|
✗ |
if(model->voxtype) { |
4447 |
|
✗ |
for(i = 0; i < model->numvoxtype; i++) |
4448 |
|
✗ |
if(model->voxtype[i].item) |
4449 |
|
✗ |
M3D_FREE(model->voxtype[i].item); |
4450 |
|
✗ |
M3D_FREE(model->voxtype); |
4451 |
|
|
} |
4452 |
|
✗ |
if(model->voxel) { |
4453 |
|
✗ |
for(i = 0; i < model->numvoxel; i++) |
4454 |
|
✗ |
if(model->voxel[i].data) |
4455 |
|
✗ |
M3D_FREE(model->voxel[i].data); |
4456 |
|
✗ |
M3D_FREE(model->voxel); |
4457 |
|
|
} |
4458 |
|
✗ |
if(model->shape) { |
4459 |
|
✗ |
for(i = 0; i < model->numshape; i++) { |
4460 |
|
✗ |
if(model->shape[i].cmd) { |
4461 |
|
✗ |
for(j = 0; j < model->shape[i].numcmd; j++) |
4462 |
|
✗ |
if(model->shape[i].cmd[j].arg) M3D_FREE(model->shape[i].cmd[j].arg); |
4463 |
|
✗ |
M3D_FREE(model->shape[i].cmd); |
4464 |
|
|
} |
4465 |
|
|
} |
4466 |
|
✗ |
M3D_FREE(model->shape); |
4467 |
|
|
} |
4468 |
|
✗ |
if(model->material && !(model->flags & M3D_FLG_MTLLIB)) { |
4469 |
|
✗ |
for(i = 0; i < model->nummaterial; i++) |
4470 |
|
✗ |
if(model->material[i].prop) M3D_FREE(model->material[i].prop); |
4471 |
|
✗ |
M3D_FREE(model->material); |
4472 |
|
|
} |
4473 |
|
✗ |
if(model->texture) { |
4474 |
|
✗ |
for(i = 0; i < model->numtexture; i++) |
4475 |
|
✗ |
if(model->texture[i].d) M3D_FREE(model->texture[i].d); |
4476 |
|
✗ |
M3D_FREE(model->texture); |
4477 |
|
|
} |
4478 |
|
✗ |
if(model->action) { |
4479 |
|
✗ |
for(i = 0; i < model->numaction; i++) { |
4480 |
|
✗ |
if(model->action[i].frame) { |
4481 |
|
✗ |
for(j = 0; j < model->action[i].numframe; j++) |
4482 |
|
✗ |
if(model->action[i].frame[j].transform) M3D_FREE(model->action[i].frame[j].transform); |
4483 |
|
✗ |
M3D_FREE(model->action[i].frame); |
4484 |
|
|
} |
4485 |
|
|
} |
4486 |
|
✗ |
M3D_FREE(model->action); |
4487 |
|
|
} |
4488 |
|
✗ |
if(model->label) M3D_FREE(model->label); |
4489 |
|
✗ |
if(model->inlined) M3D_FREE(model->inlined); |
4490 |
|
✗ |
if(model->extra) M3D_FREE(model->extra); |
4491 |
|
✗ |
free(model); |
4492 |
|
|
} |
4493 |
|
|
#endif |
4494 |
|
|
|
4495 |
|
|
#ifdef M3D_EXPORTER |
4496 |
|
|
typedef struct { |
4497 |
|
|
char *str; |
4498 |
|
|
uint32_t offs; |
4499 |
|
|
} m3dstr_t; |
4500 |
|
|
|
4501 |
|
|
typedef struct { |
4502 |
|
|
m3dti_t data; |
4503 |
|
|
M3D_INDEX oldidx; |
4504 |
|
|
M3D_INDEX newidx; |
4505 |
|
|
} m3dtisave_t; |
4506 |
|
|
|
4507 |
|
|
typedef struct { |
4508 |
|
|
m3dv_t data; |
4509 |
|
|
M3D_INDEX oldidx; |
4510 |
|
|
M3D_INDEX newidx; |
4511 |
|
|
unsigned char norm; |
4512 |
|
|
} m3dvsave_t; |
4513 |
|
|
|
4514 |
|
|
typedef struct { |
4515 |
|
|
m3ds_t data; |
4516 |
|
|
M3D_INDEX oldidx; |
4517 |
|
|
M3D_INDEX newidx; |
4518 |
|
|
} m3dssave_t; |
4519 |
|
|
|
4520 |
|
|
typedef struct { |
4521 |
|
|
m3df_t data; |
4522 |
|
|
int group; |
4523 |
|
|
uint8_t opacity; |
4524 |
|
|
} m3dfsave_t; |
4525 |
|
|
|
4526 |
|
|
/* create unique list of strings */ |
4527 |
|
|
static m3dstr_t *_m3d_addstr(m3dstr_t *str, uint32_t *numstr, char *s) |
4528 |
|
|
{ |
4529 |
|
|
uint32_t i; |
4530 |
|
|
if(!s || !*s) return str; |
4531 |
|
|
if(str) { |
4532 |
|
|
for(i = 0; i < *numstr; i++) |
4533 |
|
|
if(str[i].str == s || !strcmp(str[i].str, s)) return str; |
4534 |
|
|
} |
4535 |
|
|
str = (m3dstr_t*)M3D_REALLOC(str, ((*numstr) + 1) * sizeof(m3dstr_t)); |
4536 |
|
|
str[*numstr].str = s; |
4537 |
|
|
str[*numstr].offs = 0; |
4538 |
|
|
(*numstr)++; |
4539 |
|
|
return str; |
4540 |
|
|
} |
4541 |
|
|
|
4542 |
|
|
/* add strings to header */ |
4543 |
|
|
m3dhdr_t *_m3d_addhdr(m3dhdr_t *h, m3dstr_t *s) |
4544 |
|
|
{ |
4545 |
|
|
int i; |
4546 |
|
|
char *safe = _m3d_safestr(s->str, 0); |
4547 |
|
|
i = (int)strlen(safe); |
4548 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, h->length + i+1); |
4549 |
|
|
if(!h) { M3D_FREE(safe); return NULL; } |
4550 |
|
|
memcpy((uint8_t*)h + h->length, safe, i+1); |
4551 |
|
|
s->offs = h->length - 16; |
4552 |
|
|
h->length += i+1; |
4553 |
|
|
M3D_FREE(safe); |
4554 |
|
|
return h; |
4555 |
|
|
} |
4556 |
|
|
|
4557 |
|
|
/* return offset of string */ |
4558 |
|
|
static uint32_t _m3d_stridx(m3dstr_t *str, uint32_t numstr, char *s) |
4559 |
|
|
{ |
4560 |
|
|
uint32_t i; |
4561 |
|
|
char *safe; |
4562 |
|
|
if(!s || !*s) return 0; |
4563 |
|
|
if(str) { |
4564 |
|
|
safe = _m3d_safestr(s, 0); |
4565 |
|
|
if(!safe) return 0; |
4566 |
|
|
if(!*safe) { |
4567 |
|
|
free(safe); |
4568 |
|
|
return 0; |
4569 |
|
|
} |
4570 |
|
|
for(i = 0; i < numstr; i++) |
4571 |
|
|
if(!strcmp(str[i].str, s)) { |
4572 |
|
|
free(safe); |
4573 |
|
|
return str[i].offs; |
4574 |
|
|
} |
4575 |
|
|
free(safe); |
4576 |
|
|
} |
4577 |
|
|
return 0; |
4578 |
|
|
} |
4579 |
|
|
|
4580 |
|
|
/* compare to faces by their material */ |
4581 |
|
|
static int _m3d_facecmp(const void *a, const void *b) { |
4582 |
|
|
const m3dfsave_t *A = (const m3dfsave_t*)a, *B = (const m3dfsave_t*)b; |
4583 |
|
|
return A->group != B->group ? A->group - B->group : (A->opacity != B->opacity ? (int)B->opacity - (int)A->opacity : |
4584 |
|
|
(int)A->data.materialid - (int)B->data.materialid); |
4585 |
|
|
} |
4586 |
|
|
/* compare face groups */ |
4587 |
|
|
static int _m3d_grpcmp(const void *a, const void *b) { return *((uint32_t*)a) - *((uint32_t*)b); } |
4588 |
|
|
/* compare UVs */ |
4589 |
|
|
static int _m3d_ticmp(const void *a, const void *b) { return memcmp(a, b, sizeof(m3dti_t)); } |
4590 |
|
|
/* compare skin groups */ |
4591 |
|
|
static int _m3d_skincmp(const void *a, const void *b) { return memcmp(a, b, sizeof(m3ds_t)); } |
4592 |
|
|
/* compare vertices */ |
4593 |
|
|
static int _m3d_vrtxcmp(const void *a, const void *b) { |
4594 |
|
|
int c = memcmp(a, b, 3 * sizeof(M3D_FLOAT)); |
4595 |
|
|
if(c) return c; |
4596 |
|
|
c = ((m3dvsave_t*)a)->norm - ((m3dvsave_t*)b)->norm; |
4597 |
|
|
if(c) return c; |
4598 |
|
|
return memcmp(a, b, sizeof(m3dv_t)); |
4599 |
|
|
} |
4600 |
|
|
/* compare labels */ |
4601 |
|
|
static _inline int _m3d_strcmp(char *a, char *b) |
4602 |
|
|
{ |
4603 |
|
|
if(a == NULL && b != NULL) return -1; |
4604 |
|
|
if(a != NULL && b == NULL) return 1; |
4605 |
|
|
if(a == NULL && b == NULL) return 0; |
4606 |
|
|
return strcmp(a, b); |
4607 |
|
|
} |
4608 |
|
|
static int _m3d_lblcmp(const void *a, const void *b) { |
4609 |
|
|
const m3dl_t *A = (const m3dl_t*)a, *B = (const m3dl_t*)b; |
4610 |
|
|
int c = _m3d_strcmp(A->lang, B->lang); |
4611 |
|
|
if(!c) c = _m3d_strcmp(A->name, B->name); |
4612 |
|
|
if(!c) c = _m3d_strcmp(A->text, B->text); |
4613 |
|
|
return c; |
4614 |
|
|
} |
4615 |
|
|
/* compare two colors by HSV value */ |
4616 |
|
|
_inline static int _m3d_cmapcmp(const void *a, const void *b) |
4617 |
|
|
{ |
4618 |
|
|
uint8_t *A = (uint8_t*)a, *B = (uint8_t*)b; |
4619 |
|
|
_register int m, vA, vB; |
4620 |
|
|
/* get HSV value for A */ |
4621 |
|
|
m = A[2] < A[1]? A[2] : A[1]; if(A[0] < m) m = A[0]; |
4622 |
|
|
vA = A[2] > A[1]? A[2] : A[1]; if(A[0] > vA) vA = A[0]; |
4623 |
|
|
/* get HSV value for B */ |
4624 |
|
|
m = B[2] < B[1]? B[2] : B[1]; if(B[0] < m) m = B[0]; |
4625 |
|
|
vB = B[2] > B[1]? B[2] : B[1]; if(B[0] > vB) vB = B[0]; |
4626 |
|
|
return vA - vB; |
4627 |
|
|
} |
4628 |
|
|
|
4629 |
|
|
/* create sorted list of colors */ |
4630 |
|
|
static uint32_t *_m3d_addcmap(uint32_t *cmap, uint32_t *numcmap, uint32_t color) |
4631 |
|
|
{ |
4632 |
|
|
uint32_t i; |
4633 |
|
|
if(cmap) { |
4634 |
|
|
for(i = 0; i < *numcmap; i++) |
4635 |
|
|
if(cmap[i] == color) return cmap; |
4636 |
|
|
} |
4637 |
|
|
cmap = (uint32_t*)M3D_REALLOC(cmap, ((*numcmap) + 1) * sizeof(uint32_t)); |
4638 |
|
|
for(i = 0; i < *numcmap && _m3d_cmapcmp(&color, &cmap[i]) > 0; i++); |
4639 |
|
|
if(i < *numcmap) memmove(&cmap[i+1], &cmap[i], ((*numcmap) - i)*sizeof(uint32_t)); |
4640 |
|
|
cmap[i] = color; |
4641 |
|
|
(*numcmap)++; |
4642 |
|
|
return cmap; |
4643 |
|
|
} |
4644 |
|
|
|
4645 |
|
|
/* look up a color and return its index */ |
4646 |
|
|
static uint32_t _m3d_cmapidx(uint32_t *cmap, uint32_t numcmap, uint32_t color) |
4647 |
|
|
{ |
4648 |
|
|
uint32_t i; |
4649 |
|
|
if(numcmap >= 65536) |
4650 |
|
|
return color; |
4651 |
|
|
for(i = 0; i < numcmap; i++) |
4652 |
|
|
if(cmap[i] == color) return i; |
4653 |
|
|
return 0; |
4654 |
|
|
} |
4655 |
|
|
|
4656 |
|
|
/* add index to output */ |
4657 |
|
|
static unsigned char *_m3d_addidx(unsigned char *out, char type, uint32_t idx) { |
4658 |
|
|
switch(type) { |
4659 |
|
|
case 1: *out++ = (uint8_t)(idx); break; |
4660 |
|
|
case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break; |
4661 |
|
|
case 4: *((uint32_t*)out) = (uint32_t)(idx); out += 4; break; |
4662 |
|
|
/* case 0: case 8: break; */ |
4663 |
|
|
} |
4664 |
|
|
return out; |
4665 |
|
|
} |
4666 |
|
|
|
4667 |
|
|
/* round a vertex position */ |
4668 |
|
|
static void _m3d_round(int quality, m3dv_t *src, m3dv_t *dst) |
4669 |
|
|
{ |
4670 |
|
|
_register int t; |
4671 |
|
|
/* copy additional attributes */ |
4672 |
|
|
if(src != dst) memcpy(dst, src, sizeof(m3dv_t)); |
4673 |
|
|
/* round according to quality */ |
4674 |
|
|
switch(quality) { |
4675 |
|
|
case M3D_EXP_INT8: |
4676 |
|
|
t = (int)(src->x * 127 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)127.0; |
4677 |
|
|
t = (int)(src->y * 127 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)127.0; |
4678 |
|
|
t = (int)(src->z * 127 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)127.0; |
4679 |
|
|
t = (int)(src->w * 127 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)127.0; |
4680 |
|
|
break; |
4681 |
|
|
case M3D_EXP_INT16: |
4682 |
|
|
t = (int)(src->x * 32767 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; |
4683 |
|
|
t = (int)(src->y * 32767 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; |
4684 |
|
|
t = (int)(src->z * 32767 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; |
4685 |
|
|
t = (int)(src->w * 32767 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)32767.0; |
4686 |
|
|
break; |
4687 |
|
|
} |
4688 |
|
|
if(dst->x == (M3D_FLOAT)-0.0) dst->x = (M3D_FLOAT)0.0; |
4689 |
|
|
if(dst->y == (M3D_FLOAT)-0.0) dst->y = (M3D_FLOAT)0.0; |
4690 |
|
|
if(dst->z == (M3D_FLOAT)-0.0) dst->z = (M3D_FLOAT)0.0; |
4691 |
|
|
if(dst->w == (M3D_FLOAT)-0.0) dst->w = (M3D_FLOAT)0.0; |
4692 |
|
|
} |
4693 |
|
|
|
4694 |
|
|
#ifdef M3D_ASCII |
4695 |
|
|
/* add a bone to ascii output */ |
4696 |
|
|
static char *_m3d_prtbone(char *ptr, m3db_t *bone, M3D_INDEX numbone, M3D_INDEX parent, uint32_t level, M3D_INDEX *vrtxidx) |
4697 |
|
|
{ |
4698 |
|
|
uint32_t i, j; |
4699 |
|
|
char *sn; |
4700 |
|
|
|
4701 |
|
|
if(level > M3D_BONEMAXLEVEL || !bone) return ptr; |
4702 |
|
|
for(i = 0; i < numbone; i++) { |
4703 |
|
|
if(bone[i].parent == parent) { |
4704 |
|
|
for(j = 0; j < level; j++) *ptr++ = '/'; |
4705 |
|
|
sn = _m3d_safestr(bone[i].name, 0); |
4706 |
|
|
ptr += sprintf(ptr, "%d %d %s\r\n", vrtxidx[bone[i].pos], vrtxidx[bone[i].ori], sn); |
4707 |
|
|
M3D_FREE(sn); |
4708 |
|
|
ptr = _m3d_prtbone(ptr, bone, numbone, i, level + 1, vrtxidx); |
4709 |
|
|
} |
4710 |
|
|
} |
4711 |
|
|
return ptr; |
4712 |
|
|
} |
4713 |
|
|
#endif |
4714 |
|
|
|
4715 |
|
|
/** |
4716 |
|
|
* Function to encode an in-memory model into on storage Model 3D format |
4717 |
|
|
*/ |
4718 |
|
|
unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size) |
4719 |
|
|
{ |
4720 |
|
|
#ifdef M3D_ASCII |
4721 |
|
|
const char *ol; |
4722 |
|
|
char *ptr; |
4723 |
|
|
#endif |
4724 |
|
|
char vc_s, vi_s, si_s, ci_s, ti_s, bi_s, nb_s, sk_s, fc_s, hi_s, fi_s, vd_s, vp_s; |
4725 |
|
|
char *sn = NULL, *sl = NULL, *sa = NULL, *sd = NULL; |
4726 |
|
|
unsigned char *out = NULL, *z = NULL, weights[M3D_NUMBONE < 8 ? 8 : M3D_NUMBONE], *norm = NULL; |
4727 |
|
|
unsigned int i, j, k, l, n, o, len, chunklen, *length; |
4728 |
|
|
int maxvox = 0, minvox = 0; |
4729 |
|
|
M3D_FLOAT scale = (M3D_FLOAT)0.0, min_x, max_x, min_y, max_y, min_z, max_z; |
4730 |
|
|
M3D_INDEX last, *vrtxidx = NULL, *mtrlidx = NULL, *tmapidx = NULL, *skinidx = NULL; |
4731 |
|
|
#ifdef M3D_VERTEXMAX |
4732 |
|
|
M3D_INDEX lastp; |
4733 |
|
|
#endif |
4734 |
|
|
uint32_t idx, numcmap = 0, *cmap = NULL, numvrtx = 0, maxvrtx = 0, numtmap = 0, maxtmap = 0, numproc = 0; |
4735 |
|
|
uint32_t numskin = 0, maxskin = 0, numstr = 0, maxt = 0, maxbone = 0, numgrp = 0, maxgrp = 0, *grpidx = NULL; |
4736 |
|
|
uint8_t *opa; |
4737 |
|
|
m3dcd_t *cd; |
4738 |
|
|
m3dc_t *cmd; |
4739 |
|
|
m3dstr_t *str = NULL; |
4740 |
|
|
m3dvsave_t *vrtx = NULL, vertex; |
4741 |
|
|
m3dtisave_t *tmap = NULL, tcoord; |
4742 |
|
|
m3dssave_t *skin = NULL, sk; |
4743 |
|
|
m3dfsave_t *face = NULL; |
4744 |
|
|
m3dhdr_t *h = NULL; |
4745 |
|
|
m3dm_t *m; |
4746 |
|
|
m3da_t *a; |
4747 |
|
|
|
4748 |
|
|
if(!model) { |
4749 |
|
|
if(size) *size = 0; |
4750 |
|
|
return NULL; |
4751 |
|
|
} |
4752 |
|
|
model->errcode = M3D_SUCCESS; |
4753 |
|
|
#ifdef M3D_ASCII |
4754 |
|
|
if(flags & M3D_EXP_ASCII) quality = M3D_EXP_DOUBLE; |
4755 |
|
|
#endif |
4756 |
|
|
vrtxidx = (M3D_INDEX*)M3D_MALLOC(model->numvertex * sizeof(M3D_INDEX)); |
4757 |
|
|
if(!vrtxidx) goto memerr; |
4758 |
|
|
memset(vrtxidx, 255, model->numvertex * sizeof(M3D_INDEX)); |
4759 |
|
|
if(model->numvertex && !(flags & M3D_EXP_NONORMAL)){ |
4760 |
|
|
norm = (unsigned char*)M3D_MALLOC(model->numvertex * sizeof(unsigned char)); |
4761 |
|
|
if(!norm) goto memerr; |
4762 |
|
|
memset(norm, 0, model->numvertex * sizeof(unsigned char)); |
4763 |
|
|
} |
4764 |
|
|
if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) { |
4765 |
|
|
mtrlidx = (M3D_INDEX*)M3D_MALLOC(model->nummaterial * sizeof(M3D_INDEX)); |
4766 |
|
|
if(!mtrlidx) goto memerr; |
4767 |
|
|
memset(mtrlidx, 255, model->nummaterial * sizeof(M3D_INDEX)); |
4768 |
|
|
opa = (uint8_t*)M3D_MALLOC(model->nummaterial * 2 * sizeof(M3D_INDEX)); |
4769 |
|
|
if(!opa) goto memerr; |
4770 |
|
|
memset(opa, 255, model->nummaterial * 2 * sizeof(M3D_INDEX)); |
4771 |
|
|
} |
4772 |
|
|
if(model->numtmap && !(flags & M3D_EXP_NOTXTCRD)) { |
4773 |
|
|
tmapidx = (M3D_INDEX*)M3D_MALLOC(model->numtmap * sizeof(M3D_INDEX)); |
4774 |
|
|
if(!tmapidx) goto memerr; |
4775 |
|
|
memset(tmapidx, 255, model->numtmap * sizeof(M3D_INDEX)); |
4776 |
|
|
} |
4777 |
|
|
/** collect array elements that are actually referenced **/ |
4778 |
|
|
if(!(flags & M3D_EXP_NOFACE)) { |
4779 |
|
|
/* face */ |
4780 |
|
|
if(model->numface && model->face) { |
4781 |
|
|
M3D_LOG("Processing mesh face"); |
4782 |
|
|
face = (m3dfsave_t*)M3D_MALLOC(model->numface * sizeof(m3dfsave_t)); |
4783 |
|
|
if(!face) goto memerr; |
4784 |
|
|
for(i = 0; i < model->numface; i++) { |
4785 |
|
|
memcpy(&face[i].data, &model->face[i], sizeof(m3df_t)); |
4786 |
|
|
face[i].group = 0; |
4787 |
|
|
face[i].opacity = 255; |
4788 |
|
|
if(!(flags & M3D_EXP_NOMATERIAL) && model->face[i].materialid < model->nummaterial) { |
4789 |
|
|
if(model->material[model->face[i].materialid].numprop) { |
4790 |
|
|
mtrlidx[model->face[i].materialid] = 0; |
4791 |
|
|
if(opa[model->face[i].materialid * 2]) { |
4792 |
|
|
m = &model->material[model->face[i].materialid]; |
4793 |
|
|
for(j = 0; j < m->numprop; j++) |
4794 |
|
|
if(m->prop[j].type == m3dp_Kd) { |
4795 |
|
|
opa[model->face[i].materialid * 2 + 1] = ((uint8_t*)&m->prop[j].value.color)[3]; |
4796 |
|
|
break; |
4797 |
|
|
} |
4798 |
|
|
for(j = 0; j < m->numprop; j++) |
4799 |
|
|
if(m->prop[j].type == m3dp_d) { |
4800 |
|
|
opa[model->face[i].materialid * 2 + 1] = (uint8_t)(m->prop[j].value.fnum * 255); |
4801 |
|
|
break; |
4802 |
|
|
} |
4803 |
|
|
opa[model->face[i].materialid * 2] = 0; |
4804 |
|
|
} |
4805 |
|
|
face[i].opacity = opa[model->face[i].materialid * 2 + 1]; |
4806 |
|
|
} else |
4807 |
|
|
face[i].data.materialid = M3D_UNDEF; |
4808 |
|
|
} |
4809 |
|
|
for(j = 0; j < 3; j++) { |
4810 |
|
|
k = model->face[i].vertex[j]; |
4811 |
|
|
if(k < model->numvertex) |
4812 |
|
|
vrtxidx[k] = 0; |
4813 |
|
|
if(!(flags & M3D_EXP_NOCMAP)) { |
4814 |
|
|
cmap = _m3d_addcmap(cmap, &numcmap, model->vertex[k].color); |
4815 |
|
|
if(!cmap) goto memerr; |
4816 |
|
|
} |
4817 |
|
|
k = model->face[i].normal[j]; |
4818 |
|
|
if(k < model->numvertex && !(flags & M3D_EXP_NONORMAL)) { |
4819 |
|
|
vrtxidx[k] = 0; |
4820 |
|
|
norm[k] = 1; |
4821 |
|
|
} |
4822 |
|
|
k = model->face[i].texcoord[j]; |
4823 |
|
|
if(k < model->numtmap && !(flags & M3D_EXP_NOTXTCRD)) |
4824 |
|
|
tmapidx[k] = 0; |
4825 |
|
|
#ifdef M3D_VERTEXMAX |
4826 |
|
|
k = model->face[i].vertmax[j]; |
4827 |
|
|
if(k < model->numvertex && !(flags & M3D_EXP_NOVRTMAX)) |
4828 |
|
|
vrtxidx[k] = 0; |
4829 |
|
|
#endif |
4830 |
|
|
} |
4831 |
|
|
/* convert from CW to CCW */ |
4832 |
|
|
if(flags & M3D_EXP_IDOSUCK) { |
4833 |
|
|
j = face[i].data.vertex[1]; |
4834 |
|
|
face[i].data.vertex[1] = face[i].data.vertex[2]; |
4835 |
|
|
face[i].data.vertex[2] = j; |
4836 |
|
|
j = face[i].data.normal[1]; |
4837 |
|
|
face[i].data.normal[1] = face[i].data.normal[2]; |
4838 |
|
|
face[i].data.normal[2] = j; |
4839 |
|
|
j = face[i].data.texcoord[1]; |
4840 |
|
|
face[i].data.texcoord[1] = face[i].data.texcoord[2]; |
4841 |
|
|
face[i].data.texcoord[2] = j; |
4842 |
|
|
#ifdef M3D_VERTEXMAX |
4843 |
|
|
j = face[i].data.vertmax[1]; |
4844 |
|
|
face[i].data.vertmax[1] = face[i].data.vertmax[2]; |
4845 |
|
|
face[i].data.vertmax[2] = j; |
4846 |
|
|
#endif |
4847 |
|
|
} |
4848 |
|
|
} |
4849 |
|
|
} |
4850 |
|
|
if((model->numvoxtype && model->voxtype) || (model->numvoxel && model->voxel)) { |
4851 |
|
|
M3D_LOG("Processing voxel face"); |
4852 |
|
|
for(i = 0; i < model->numvoxtype; i++) { |
4853 |
|
|
str = _m3d_addstr(str, &numstr, model->voxtype[i].name); |
4854 |
|
|
if(model->voxtype[i].name && !str) goto memerr; |
4855 |
|
|
if(!(flags & M3D_EXP_NOCMAP)) { |
4856 |
|
|
cmap = _m3d_addcmap(cmap, &numcmap, model->voxtype[i].color); |
4857 |
|
|
if(!cmap) goto memerr; |
4858 |
|
|
} |
4859 |
|
|
for(j = 0; j < model->voxtype[i].numitem; j++) { |
4860 |
|
|
str = _m3d_addstr(str, &numstr, model->voxtype[i].item[j].name); |
4861 |
|
|
if(model->voxtype[i].item[j].name && !str) goto memerr; |
4862 |
|
|
} |
4863 |
|
|
} |
4864 |
|
|
for(i = 0; i < model->numvoxel; i++) { |
4865 |
|
|
str = _m3d_addstr(str, &numstr, model->voxel[i].name); |
4866 |
|
|
if(model->voxel[i].name && !str) goto memerr; |
4867 |
|
|
if(model->voxel[i].x < minvox) minvox = model->voxel[i].x; |
4868 |
|
|
if(model->voxel[i].x + (int)model->voxel[i].w > maxvox) maxvox = model->voxel[i].x + model->voxel[i].w; |
4869 |
|
|
if(model->voxel[i].y < minvox) minvox = model->voxel[i].y; |
4870 |
|
|
if(model->voxel[i].y + (int)model->voxel[i].h > maxvox) maxvox = model->voxel[i].y + model->voxel[i].h; |
4871 |
|
|
if(model->voxel[i].z < minvox) minvox = model->voxel[i].z; |
4872 |
|
|
if(model->voxel[i].z + (int)model->voxel[i].d > maxvox) maxvox = model->voxel[i].z + model->voxel[i].d; |
4873 |
|
|
} |
4874 |
|
|
} |
4875 |
|
|
if(model->numshape && model->shape) { |
4876 |
|
|
M3D_LOG("Processing shape face"); |
4877 |
|
|
for(i = 0; i < model->numshape; i++) { |
4878 |
|
|
if(!model->shape[i].numcmd) continue; |
4879 |
|
|
str = _m3d_addstr(str, &numstr, model->shape[i].name); |
4880 |
|
|
if(model->shape[i].name && !str) goto memerr; |
4881 |
|
|
for(j = 0; j < model->shape[i].numcmd; j++) { |
4882 |
|
|
cmd = &model->shape[i].cmd[j]; |
4883 |
|
|
if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg) |
4884 |
|
|
continue; |
4885 |
|
|
if(cmd->type == m3dc_mesh) { |
4886 |
|
|
if(numgrp + 2 < maxgrp) { |
4887 |
|
|
maxgrp += 1024; |
4888 |
|
|
grpidx = (uint32_t*)realloc(grpidx, maxgrp * sizeof(uint32_t)); |
4889 |
|
|
if(!grpidx) goto memerr; |
4890 |
|
|
if(!numgrp) { |
4891 |
|
|
grpidx[0] = 0; |
4892 |
|
|
grpidx[1] = model->numface; |
4893 |
|
|
numgrp += 2; |
4894 |
|
|
} |
4895 |
|
|
} |
4896 |
|
|
grpidx[numgrp + 0] = cmd->arg[0]; |
4897 |
|
|
grpidx[numgrp + 1] = cmd->arg[0] + cmd->arg[1]; |
4898 |
|
|
numgrp += 2; |
4899 |
|
|
} |
4900 |
|
|
cd = &m3d_commandtypes[cmd->type]; |
4901 |
|
|
for(k = n = 0, l = cd->p; k < l; k++) |
4902 |
|
|
switch(cd->a[((k - n) % (cd->p - n)) + n]) { |
4903 |
|
|
case m3dcp_mi_t: |
4904 |
|
|
if(!(flags & M3D_EXP_NOMATERIAL) && cmd->arg[k] < model->nummaterial) |
4905 |
|
|
mtrlidx[cmd->arg[k]] = 0; |
4906 |
|
|
break; |
4907 |
|
|
case m3dcp_ti_t: |
4908 |
|
|
if(!(flags & M3D_EXP_NOTXTCRD) && cmd->arg[k] < model->numtmap) |
4909 |
|
|
tmapidx[cmd->arg[k]] = 0; |
4910 |
|
|
break; |
4911 |
|
|
case m3dcp_qi_t: |
4912 |
|
|
case m3dcp_vi_t: |
4913 |
|
|
if(cmd->arg[k] < model->numvertex) |
4914 |
|
|
vrtxidx[cmd->arg[k]] = 0; |
4915 |
|
|
break; |
4916 |
|
|
case m3dcp_va_t: |
4917 |
|
|
n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1); |
4918 |
|
|
break; |
4919 |
|
|
} |
4920 |
|
|
} |
4921 |
|
|
} |
4922 |
|
|
} |
4923 |
|
|
if(model->numface && face) { |
4924 |
|
|
if(numgrp && grpidx) { |
4925 |
|
|
qsort(grpidx, numgrp, sizeof(uint32_t), _m3d_grpcmp); |
4926 |
|
|
for(i = j = 0; i < model->numface && j < numgrp; i++) { |
4927 |
|
|
while(j < numgrp && grpidx[j] < i) j++; |
4928 |
|
|
face[i].group = j; |
4929 |
|
|
} |
4930 |
|
|
} |
4931 |
|
|
qsort(face, model->numface, sizeof(m3dfsave_t), _m3d_facecmp); |
4932 |
|
|
} |
4933 |
|
|
if(grpidx) { M3D_FREE(grpidx); grpidx = NULL; } |
4934 |
|
|
if(model->numlabel && model->label) { |
4935 |
|
|
M3D_LOG("Processing annotation labels"); |
4936 |
|
|
for(i = 0; i < model->numlabel; i++) { |
4937 |
|
|
str = _m3d_addstr(str, &numstr, model->label[i].name); |
4938 |
|
|
str = _m3d_addstr(str, &numstr, model->label[i].lang); |
4939 |
|
|
str = _m3d_addstr(str, &numstr, model->label[i].text); |
4940 |
|
|
if(!(flags & M3D_EXP_NOCMAP)) { |
4941 |
|
|
cmap = _m3d_addcmap(cmap, &numcmap, model->label[i].color); |
4942 |
|
|
if(!cmap) goto memerr; |
4943 |
|
|
} |
4944 |
|
|
if(model->label[i].vertexid < model->numvertex) |
4945 |
|
|
vrtxidx[model->label[i].vertexid] = 0; |
4946 |
|
|
} |
4947 |
|
|
qsort(model->label, model->numlabel, sizeof(m3dl_t), _m3d_lblcmp); |
4948 |
|
|
} |
4949 |
|
|
} else if(!(flags & M3D_EXP_NOMATERIAL)) { |
4950 |
|
|
/* without a face, simply add all materials, because it can be an mtllib */ |
4951 |
|
|
for(i = 0; i < model->nummaterial; i++) |
4952 |
|
|
mtrlidx[i] = i; |
4953 |
|
|
} |
4954 |
|
|
/* bind-pose skeleton */ |
4955 |
|
|
if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) { |
4956 |
|
|
M3D_LOG("Processing bones"); |
4957 |
|
|
for(i = 0; i < model->numbone; i++) { |
4958 |
|
|
str = _m3d_addstr(str, &numstr, model->bone[i].name); |
4959 |
|
|
if(!str) goto memerr; |
4960 |
|
|
k = model->bone[i].pos; |
4961 |
|
|
if(k < model->numvertex) |
4962 |
|
|
vrtxidx[k] = 0; |
4963 |
|
|
k = model->bone[i].ori; |
4964 |
|
|
if(k < model->numvertex) |
4965 |
|
|
vrtxidx[k] = 0; |
4966 |
|
|
} |
4967 |
|
|
} |
4968 |
|
|
/* actions, animated skeleton poses */ |
4969 |
|
|
if(model->numaction && model->action && !(flags & M3D_EXP_NOACTION)) { |
4970 |
|
|
M3D_LOG("Processing action list"); |
4971 |
|
|
for(j = 0; j < model->numaction; j++) { |
4972 |
|
|
a = &model->action[j]; |
4973 |
|
|
str = _m3d_addstr(str, &numstr, a->name); |
4974 |
|
|
if(!str) goto memerr; |
4975 |
|
|
if(a->numframe > 65535) a->numframe = 65535; |
4976 |
|
|
for(i = 0; i < a->numframe; i++) { |
4977 |
|
|
for(l = 0; l < a->frame[i].numtransform; l++) { |
4978 |
|
|
k = a->frame[i].transform[l].pos; |
4979 |
|
|
if(k < model->numvertex) |
4980 |
|
|
vrtxidx[k] = 0; |
4981 |
|
|
k = a->frame[i].transform[l].ori; |
4982 |
|
|
if(k < model->numvertex) |
4983 |
|
|
vrtxidx[k] = 0; |
4984 |
|
|
} |
4985 |
|
|
if(l > maxt) maxt = l; |
4986 |
|
|
} |
4987 |
|
|
} |
4988 |
|
|
} |
4989 |
|
|
/* add colors to color map and texture names to string table */ |
4990 |
|
|
if(!(flags & M3D_EXP_NOMATERIAL)) { |
4991 |
|
|
M3D_LOG("Processing materials"); |
4992 |
|
|
for(i = k = 0; i < model->nummaterial; i++) { |
4993 |
|
|
if(mtrlidx[i] == M3D_UNDEF || !model->material[i].numprop) continue; |
4994 |
|
|
mtrlidx[i] = k++; |
4995 |
|
|
m = &model->material[i]; |
4996 |
|
|
str = _m3d_addstr(str, &numstr, m->name); |
4997 |
|
|
if(!str) goto memerr; |
4998 |
|
|
if(m->prop) |
4999 |
|
|
for(j = 0; j < m->numprop; j++) { |
5000 |
|
|
if(!(flags & M3D_EXP_NOCMAP) && m->prop[j].type < 128) { |
5001 |
|
|
for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) { |
5002 |
|
|
if(m->prop[j].type == m3d_propertytypes[l].id && m3d_propertytypes[l].format == m3dpf_color) { |
5003 |
|
|
((uint8_t*)&m->prop[j].value.color)[3] = opa[i * 2 + 1]; |
5004 |
|
|
cmap = _m3d_addcmap(cmap, &numcmap, m->prop[j].value.color); |
5005 |
|
|
if(!cmap) goto memerr; |
5006 |
|
|
break; |
5007 |
|
|
} |
5008 |
|
|
} |
5009 |
|
|
} |
5010 |
|
|
if(m->prop[j].type >= 128 && m->prop[j].value.textureid < model->numtexture && |
5011 |
|
|
model->texture[m->prop[j].value.textureid].name) { |
5012 |
|
|
str = _m3d_addstr(str, &numstr, model->texture[m->prop[j].value.textureid].name); |
5013 |
|
|
if(!str) goto memerr; |
5014 |
|
|
} |
5015 |
|
|
} |
5016 |
|
|
} |
5017 |
|
|
} |
5018 |
|
|
/* if there's only one black color, don't store it */ |
5019 |
|
|
if(numcmap == 1 && cmap && !cmap[0]) numcmap = 0; |
5020 |
|
|
|
5021 |
|
|
/** compress lists **/ |
5022 |
|
|
if(model->numtmap && !(flags & M3D_EXP_NOTXTCRD)) { |
5023 |
|
|
M3D_LOG("Compressing tmap"); |
5024 |
|
|
tmap = (m3dtisave_t*)M3D_MALLOC(model->numtmap * sizeof(m3dtisave_t)); |
5025 |
|
|
if(!tmap) goto memerr; |
5026 |
|
|
for(i = 0; i < model->numtmap; i++) { |
5027 |
|
|
if(tmapidx[i] == M3D_UNDEF) continue; |
5028 |
|
|
switch(quality) { |
5029 |
|
|
case M3D_EXP_INT8: |
5030 |
|
|
l = (unsigned int)(model->tmap[i].u * 255); tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)255.0; |
5031 |
|
|
l = (unsigned int)(model->tmap[i].v * 255); tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)255.0; |
5032 |
|
|
break; |
5033 |
|
|
case M3D_EXP_INT16: |
5034 |
|
|
l = (unsigned int)(model->tmap[i].u * 65535); tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)65535.0; |
5035 |
|
|
l = (unsigned int)(model->tmap[i].v * 65535); tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)65535.0; |
5036 |
|
|
break; |
5037 |
|
|
default: |
5038 |
|
|
tcoord.data.u = model->tmap[i].u; |
5039 |
|
|
tcoord.data.v = model->tmap[i].v; |
5040 |
|
|
break; |
5041 |
|
|
} |
5042 |
|
|
if(flags & M3D_EXP_FLIPTXTCRD) |
5043 |
|
|
tcoord.data.v = (M3D_FLOAT)1.0 - tcoord.data.v; |
5044 |
|
|
tcoord.oldidx = i; |
5045 |
|
|
memcpy(&tmap[numtmap++], &tcoord, sizeof(m3dtisave_t)); |
5046 |
|
|
} |
5047 |
|
|
if(numtmap) { |
5048 |
|
|
qsort(tmap, numtmap, sizeof(m3dtisave_t), _m3d_ticmp); |
5049 |
|
|
memcpy(&tcoord.data, &tmap[0], sizeof(m3dti_t)); |
5050 |
|
|
for(i = 0; i < numtmap; i++) { |
5051 |
|
|
if(memcmp(&tcoord.data, &tmap[i].data, sizeof(m3dti_t))) { |
5052 |
|
|
memcpy(&tcoord.data, &tmap[i].data, sizeof(m3dti_t)); |
5053 |
|
|
maxtmap++; |
5054 |
|
|
} |
5055 |
|
|
tmap[i].newidx = maxtmap; |
5056 |
|
|
tmapidx[tmap[i].oldidx] = maxtmap; |
5057 |
|
|
} |
5058 |
|
|
maxtmap++; |
5059 |
|
|
} |
5060 |
|
|
} |
5061 |
|
|
if(model->numskin && model->skin && !(flags & M3D_EXP_NOBONE)) { |
5062 |
|
|
M3D_LOG("Compressing skin"); |
5063 |
|
|
skinidx = (M3D_INDEX*)M3D_MALLOC(model->numskin * sizeof(M3D_INDEX)); |
5064 |
|
|
if(!skinidx) goto memerr; |
5065 |
|
|
skin = (m3dssave_t*)M3D_MALLOC(model->numskin * sizeof(m3dssave_t)); |
5066 |
|
|
if(!skin) goto memerr; |
5067 |
|
|
memset(skinidx, 255, model->numskin * sizeof(M3D_INDEX)); |
5068 |
|
|
for(i = 0; i < model->numvertex; i++) { |
5069 |
|
|
if(vrtxidx[i] != M3D_UNDEF && model->vertex[i].skinid < model->numskin) |
5070 |
|
|
skinidx[model->vertex[i].skinid] = 0; |
5071 |
|
|
} |
5072 |
|
|
for(i = 0; i < model->numskin; i++) { |
5073 |
|
|
if(skinidx[i] == M3D_UNDEF) continue; |
5074 |
|
|
memset(&sk, 0, sizeof(m3dssave_t)); |
5075 |
|
|
for(j = 0, min_x = (M3D_FLOAT)0.0; j < M3D_NUMBONE && model->skin[i].boneid[j] != M3D_UNDEF && |
5076 |
|
|
model->skin[i].weight[j] > (M3D_FLOAT)0.0; j++) { |
5077 |
|
|
sk.data.boneid[j] = model->skin[i].boneid[j]; |
5078 |
|
|
sk.data.weight[j] = model->skin[i].weight[j]; |
5079 |
|
|
min_x += sk.data.weight[j]; |
5080 |
|
|
} |
5081 |
|
|
if(j > maxbone) maxbone = j; |
5082 |
|
|
if(min_x != (M3D_FLOAT)1.0 && min_x != (M3D_FLOAT)0.0) |
5083 |
|
|
for(j = 0; j < M3D_NUMBONE && sk.data.weight[j] > (M3D_FLOAT)0.0; j++) |
5084 |
|
|
sk.data.weight[j] /= min_x; |
5085 |
|
|
sk.oldidx = i; |
5086 |
|
|
memcpy(&skin[numskin++], &sk, sizeof(m3dssave_t)); |
5087 |
|
|
} |
5088 |
|
|
if(numskin) { |
5089 |
|
|
qsort(skin, numskin, sizeof(m3dssave_t), _m3d_skincmp); |
5090 |
|
|
memcpy(&sk.data, &skin[0].data, sizeof(m3ds_t)); |
5091 |
|
|
for(i = 0; i < numskin; i++) { |
5092 |
|
|
if(memcmp(&sk.data, &skin[i].data, sizeof(m3ds_t))) { |
5093 |
|
|
memcpy(&sk.data, &skin[i].data, sizeof(m3ds_t)); |
5094 |
|
|
maxskin++; |
5095 |
|
|
} |
5096 |
|
|
skin[i].newidx = maxskin; |
5097 |
|
|
skinidx[skin[i].oldidx] = maxskin; |
5098 |
|
|
} |
5099 |
|
|
maxskin++; |
5100 |
|
|
} |
5101 |
|
|
} |
5102 |
|
|
|
5103 |
|
|
M3D_LOG("Compressing vertex list"); |
5104 |
|
|
min_x = min_y = min_z = (M3D_FLOAT)1e10; |
5105 |
|
|
max_x = max_y = max_z = (M3D_FLOAT)-1e10; |
5106 |
|
|
if(vrtxidx) { |
5107 |
|
|
vrtx = (m3dvsave_t*)M3D_MALLOC(model->numvertex * sizeof(m3dvsave_t)); |
5108 |
|
|
if(!vrtx) goto memerr; |
5109 |
|
|
for(i = numvrtx = 0; i < model->numvertex; i++) { |
5110 |
|
|
if(vrtxidx[i] == M3D_UNDEF) continue; |
5111 |
|
|
_m3d_round(quality, &model->vertex[i], &vertex.data); |
5112 |
|
|
vertex.norm = norm ? norm[i] : 0; |
5113 |
|
|
if(vertex.data.skinid != M3D_INDEXMAX && !vertex.norm) { |
5114 |
|
|
vertex.data.skinid = vertex.data.skinid != M3D_UNDEF && skinidx ? skinidx[vertex.data.skinid] : M3D_UNDEF; |
5115 |
|
|
if(vertex.data.x > max_x) max_x = vertex.data.x; |
5116 |
|
|
if(vertex.data.x < min_x) min_x = vertex.data.x; |
5117 |
|
|
if(vertex.data.y > max_y) max_y = vertex.data.y; |
5118 |
|
|
if(vertex.data.y < min_y) min_y = vertex.data.y; |
5119 |
|
|
if(vertex.data.z > max_z) max_z = vertex.data.z; |
5120 |
|
|
if(vertex.data.z < min_z) min_z = vertex.data.z; |
5121 |
|
|
} |
5122 |
|
|
#ifdef M3D_VERTEXTYPE |
5123 |
|
|
vertex.data.type = 0; |
5124 |
|
|
#endif |
5125 |
|
|
vertex.oldidx = i; |
5126 |
|
|
memcpy(&vrtx[numvrtx++], &vertex, sizeof(m3dvsave_t)); |
5127 |
|
|
} |
5128 |
|
|
if(numvrtx) { |
5129 |
|
|
qsort(vrtx, numvrtx, sizeof(m3dvsave_t), _m3d_vrtxcmp); |
5130 |
|
|
memcpy(&vertex.data, &vrtx[0].data, sizeof(m3dv_t)); |
5131 |
|
|
for(i = 0; i < numvrtx; i++) { |
5132 |
|
|
if(memcmp(&vertex.data, &vrtx[i].data, vrtx[i].norm ? 3 * sizeof(M3D_FLOAT) : sizeof(m3dv_t))) { |
5133 |
|
|
memcpy(&vertex.data, &vrtx[i].data, sizeof(m3dv_t)); |
5134 |
|
|
maxvrtx++; |
5135 |
|
|
} |
5136 |
|
|
vrtx[i].newidx = maxvrtx; |
5137 |
|
|
vrtxidx[vrtx[i].oldidx] = maxvrtx; |
5138 |
|
|
} |
5139 |
|
|
maxvrtx++; |
5140 |
|
|
} |
5141 |
|
|
} |
5142 |
|
|
if(norm) { M3D_FREE(norm); norm = NULL; } |
5143 |
|
|
|
5144 |
|
|
/* normalize to bounding cube */ |
5145 |
|
|
if(numvrtx && !(flags & M3D_EXP_NORECALC)) { |
5146 |
|
|
M3D_LOG("Normalizing coordinates"); |
5147 |
|
|
if(min_x < (M3D_FLOAT)0.0) min_x = -min_x; |
5148 |
|
|
if(max_x < (M3D_FLOAT)0.0) max_x = -max_x; |
5149 |
|
|
if(min_y < (M3D_FLOAT)0.0) min_y = -min_y; |
5150 |
|
|
if(max_y < (M3D_FLOAT)0.0) max_y = -max_y; |
5151 |
|
|
if(min_z < (M3D_FLOAT)0.0) min_z = -min_z; |
5152 |
|
|
if(max_z < (M3D_FLOAT)0.0) max_z = -max_z; |
5153 |
|
|
scale = min_x; |
5154 |
|
|
if(max_x > scale) scale = max_x; |
5155 |
|
|
if(min_y > scale) scale = min_y; |
5156 |
|
|
if(max_y > scale) scale = max_y; |
5157 |
|
|
if(min_z > scale) scale = min_z; |
5158 |
|
|
if(max_z > scale) scale = max_z; |
5159 |
|
|
if(scale <= (M3D_FLOAT)0.0) scale = (M3D_FLOAT)1.0; |
5160 |
|
|
if(scale != (M3D_FLOAT)1.0) { |
5161 |
|
|
for(i = 0; i < numvrtx; i++) { |
5162 |
|
|
if(vrtx[i].data.skinid == M3D_INDEXMAX) continue; |
5163 |
|
|
vrtx[i].data.x /= scale; |
5164 |
|
|
vrtx[i].data.y /= scale; |
5165 |
|
|
vrtx[i].data.z /= scale; |
5166 |
|
|
} |
5167 |
|
|
} |
5168 |
|
|
} |
5169 |
|
|
if(model->scale > (M3D_FLOAT)0.0) scale = model->scale; |
5170 |
|
|
if(scale <= (M3D_FLOAT)0.0) scale = (M3D_FLOAT)1.0; |
5171 |
|
|
|
5172 |
|
|
/* meta info */ |
5173 |
|
|
sn = _m3d_safestr(model->name && *model->name ? model->name : (char*)"(noname)", 2); |
5174 |
|
|
sl = _m3d_safestr(model->license ? model->license : (char*)"MIT", 2); |
5175 |
|
|
sa = _m3d_safestr(model->author ? model->author : getenv("LOGNAME"), 2); |
5176 |
|
|
if(!sn || !sl || !sa) { |
5177 |
|
|
memerr: if(vrtxidx) M3D_FREE(vrtxidx); |
5178 |
|
|
if(mtrlidx) M3D_FREE(mtrlidx); |
5179 |
|
|
if(tmapidx) M3D_FREE(tmapidx); |
5180 |
|
|
if(skinidx) M3D_FREE(skinidx); |
5181 |
|
|
if(grpidx) M3D_FREE(grpidx); |
5182 |
|
|
if(norm) M3D_FREE(norm); |
5183 |
|
|
if(face) M3D_FREE(face); |
5184 |
|
|
if(cmap) M3D_FREE(cmap); |
5185 |
|
|
if(tmap) M3D_FREE(tmap); |
5186 |
|
|
if(skin) M3D_FREE(skin); |
5187 |
|
|
if(str) M3D_FREE(str); |
5188 |
|
|
if(vrtx) M3D_FREE(vrtx); |
5189 |
|
|
if(sn) M3D_FREE(sn); |
5190 |
|
|
if(sl) M3D_FREE(sl); |
5191 |
|
|
if(sa) M3D_FREE(sa); |
5192 |
|
|
if(sd) M3D_FREE(sd); |
5193 |
|
|
if(out) M3D_FREE(out); |
5194 |
|
|
if(h) M3D_FREE(h); |
5195 |
|
|
M3D_LOG("Out of memory"); |
5196 |
|
|
model->errcode = M3D_ERR_ALLOC; |
5197 |
|
|
return NULL; |
5198 |
|
|
} |
5199 |
|
|
|
5200 |
|
|
M3D_LOG("Serializing model"); |
5201 |
|
|
#ifdef M3D_ASCII |
5202 |
|
|
if(flags & M3D_EXP_ASCII) { |
5203 |
|
|
/* use CRLF to make model creators on Win happy... */ |
5204 |
|
|
sd = _m3d_safestr(model->desc, 1); |
5205 |
|
|
if(!sd) goto memerr; |
5206 |
|
|
ol = setlocale(LC_NUMERIC, NULL); |
5207 |
|
|
setlocale(LC_NUMERIC, "C"); |
5208 |
|
|
/* header */ |
5209 |
|
|
len = 64 + (unsigned int)(strlen(sn) + strlen(sl) + strlen(sa) + strlen(sd)); |
5210 |
|
|
out = (unsigned char*)M3D_MALLOC(len); |
5211 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5212 |
|
|
ptr = (char*)out; |
5213 |
|
|
ptr += sprintf(ptr, "3dmodel %g\r\n%s\r\n%s\r\n%s\r\n%s\r\n\r\n", scale, |
5214 |
|
|
sn, sl, sa, sd); |
5215 |
|
|
M3D_FREE(sl); M3D_FREE(sa); M3D_FREE(sd); |
5216 |
|
|
sl = sa = sd = NULL; |
5217 |
|
|
/* preview chunk */ |
5218 |
|
|
if(model->preview.data && model->preview.length) { |
5219 |
|
|
sl = _m3d_safestr(sn, 0); |
5220 |
|
|
if(sl) { |
5221 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)20 + strlen(sl)); |
5222 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5223 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5224 |
|
|
ptr += sprintf(ptr, "Preview\r\n%s.png\r\n\r\n", sl); |
5225 |
|
|
M3D_FREE(sl); sl = NULL; |
5226 |
|
|
} |
5227 |
|
|
} |
5228 |
|
|
M3D_FREE(sn); sn = NULL; |
5229 |
|
|
/* texture map */ |
5230 |
|
|
if(numtmap && tmap && !(flags & M3D_EXP_NOTXTCRD) && !(flags & M3D_EXP_NOFACE)) { |
5231 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(maxtmap * 32) + (uintptr_t)12); |
5232 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5233 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5234 |
|
|
ptr += sprintf(ptr, "Textmap\r\n"); |
5235 |
|
|
last = M3D_UNDEF; |
5236 |
|
|
for(i = 0; i < numtmap; i++) { |
5237 |
|
|
if(tmap[i].newidx == last) continue; |
5238 |
|
|
last = tmap[i].newidx; |
5239 |
|
|
ptr += sprintf(ptr, "%g %g\r\n", tmap[i].data.u, tmap[i].data.v); |
5240 |
|
|
} |
5241 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5242 |
|
|
} |
5243 |
|
|
/* vertex chunk */ |
5244 |
|
|
if(numvrtx && vrtx && !(flags & M3D_EXP_NOFACE)) { |
5245 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(maxvrtx * 128) + (uintptr_t)10); |
5246 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5247 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5248 |
|
|
ptr += sprintf(ptr, "Vertex\r\n"); |
5249 |
|
|
last = M3D_UNDEF; |
5250 |
|
|
for(i = 0; i < numvrtx; i++) { |
5251 |
|
|
if(vrtx[i].newidx == last) continue; |
5252 |
|
|
last = vrtx[i].newidx; |
5253 |
|
|
ptr += sprintf(ptr, "%g %g %g %g", vrtx[i].data.x, vrtx[i].data.y, vrtx[i].data.z, vrtx[i].data.w); |
5254 |
|
|
if(!(flags & M3D_EXP_NOCMAP) && vrtx[i].data.color) |
5255 |
|
|
ptr += sprintf(ptr, " #%08x", vrtx[i].data.color); |
5256 |
|
|
if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin && vrtx[i].data.skinid < M3D_INDEXMAX) { |
5257 |
|
|
if(skin[vrtx[i].data.skinid].data.weight[0] == (M3D_FLOAT)1.0) |
5258 |
|
|
ptr += sprintf(ptr, " %d", skin[vrtx[i].data.skinid].data.boneid[0]); |
5259 |
|
|
else |
5260 |
|
|
for(j = 0; j < M3D_NUMBONE && skin[vrtx[i].data.skinid].data.boneid[j] != M3D_UNDEF && |
5261 |
|
|
skin[vrtx[i].data.skinid].data.weight[j] > (M3D_FLOAT)0.0; j++) |
5262 |
|
|
ptr += sprintf(ptr, " %d:%g", skin[vrtx[i].data.skinid].data.boneid[j], |
5263 |
|
|
skin[vrtx[i].data.skinid].data.weight[j]); |
5264 |
|
|
} |
5265 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5266 |
|
|
} |
5267 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5268 |
|
|
} |
5269 |
|
|
/* bones chunk */ |
5270 |
|
|
if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) { |
5271 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)9); |
5272 |
|
|
for(i = 0; i < model->numbone; i++) { |
5273 |
|
|
len += (unsigned int)strlen(model->bone[i].name) + 128; |
5274 |
|
|
} |
5275 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5276 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5277 |
|
|
ptr += sprintf(ptr, "Bones\r\n"); |
5278 |
|
|
ptr = _m3d_prtbone(ptr, model->bone, model->numbone, M3D_UNDEF, 0, vrtxidx); |
5279 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5280 |
|
|
} |
5281 |
|
|
/* materials */ |
5282 |
|
|
if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) { |
5283 |
|
|
for(j = 0; j < model->nummaterial; j++) { |
5284 |
|
|
if(mtrlidx[j] == M3D_UNDEF || !model->material[j].numprop || !model->material[j].prop) continue; |
5285 |
|
|
m = &model->material[j]; |
5286 |
|
|
sn = _m3d_safestr(m->name, 0); |
5287 |
|
|
if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5288 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)12); |
5289 |
|
|
for(i = 0; i < m->numprop; i++) { |
5290 |
|
|
if(m->prop[i].type < 128) |
5291 |
|
|
len += 32; |
5292 |
|
|
else if(m->prop[i].value.textureid < model->numtexture && model->texture[m->prop[i].value.textureid].name) |
5293 |
|
|
len += (unsigned int)strlen(model->texture[m->prop[i].value.textureid].name) + 16; |
5294 |
|
|
} |
5295 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5296 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5297 |
|
|
ptr += sprintf(ptr, "Material %s\r\n", sn); |
5298 |
|
|
M3D_FREE(sn); sn = NULL; |
5299 |
|
|
for(i = 0; i < m->numprop; i++) { |
5300 |
|
|
k = 256; |
5301 |
|
|
if(m->prop[i].type >= 128) { |
5302 |
|
|
for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) |
5303 |
|
|
if(m->prop[i].type == m3d_propertytypes[l].id) { |
5304 |
|
|
sn = m3d_propertytypes[l].key; |
5305 |
|
|
break; |
5306 |
|
|
} |
5307 |
|
|
if(!sn) |
5308 |
|
|
for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) |
5309 |
|
|
if(m->prop[i].type - 128 == m3d_propertytypes[l].id) { |
5310 |
|
|
sn = m3d_propertytypes[l].key; |
5311 |
|
|
break; |
5312 |
|
|
} |
5313 |
|
|
k = sn ? m3dpf_map : 256; |
5314 |
|
|
} else { |
5315 |
|
|
for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) |
5316 |
|
|
if(m->prop[i].type == m3d_propertytypes[l].id) { |
5317 |
|
|
sn = m3d_propertytypes[l].key; |
5318 |
|
|
k = m3d_propertytypes[l].format; |
5319 |
|
|
break; |
5320 |
|
|
} |
5321 |
|
|
} |
5322 |
|
|
switch(k) { |
5323 |
|
|
case m3dpf_color: ptr += sprintf(ptr, "%s #%08x\r\n", sn, m->prop[i].value.color); break; |
5324 |
|
|
case m3dpf_uint8: |
5325 |
|
|
case m3dpf_uint16: |
5326 |
|
|
case m3dpf_uint32: ptr += sprintf(ptr, "%s %d\r\n", sn, m->prop[i].value.num); break; |
5327 |
|
|
case m3dpf_float: ptr += sprintf(ptr, "%s %g\r\n", sn, m->prop[i].value.fnum); break; |
5328 |
|
|
case m3dpf_map: |
5329 |
|
|
if(m->prop[i].value.textureid < model->numtexture && |
5330 |
|
|
model->texture[m->prop[i].value.textureid].name) { |
5331 |
|
|
sl = _m3d_safestr(model->texture[m->prop[i].value.textureid].name, 0); |
5332 |
|
|
if(!sl) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5333 |
|
|
if(*sl) |
5334 |
|
|
ptr += sprintf(ptr, "map_%s %s\r\n", sn, sl); |
5335 |
|
|
M3D_FREE(sn); M3D_FREE(sl); sl = NULL; |
5336 |
|
|
} |
5337 |
|
|
break; |
5338 |
|
|
} |
5339 |
|
|
sn = NULL; |
5340 |
|
|
} |
5341 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5342 |
|
|
} |
5343 |
|
|
} |
5344 |
|
|
/* procedural face */ |
5345 |
|
|
if(model->numinlined && model->inlined && !(flags & M3D_EXP_NOFACE)) { |
5346 |
|
|
/* all inlined assets which are not textures should be procedural surfaces */ |
5347 |
|
|
for(j = 0; j < model->numinlined; j++) { |
5348 |
|
|
if(!model->inlined[j].name || !*model->inlined[j].name || !model->inlined[j].length || !model->inlined[j].data || |
5349 |
|
|
(model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' && model->inlined[j].data[3] == 'G')) |
5350 |
|
|
continue; |
5351 |
|
|
for(i = k = 0; i < model->numtexture; i++) { |
5352 |
|
|
if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; } |
5353 |
|
|
} |
5354 |
|
|
if(k) continue; |
5355 |
|
|
sn = _m3d_safestr(model->inlined[j].name, 0); |
5356 |
|
|
if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5357 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)18); |
5358 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5359 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5360 |
|
|
ptr += sprintf(ptr, "Procedural\r\n%s\r\n\r\n", sn); |
5361 |
|
|
M3D_FREE(sn); sn = NULL; |
5362 |
|
|
} |
5363 |
|
|
} |
5364 |
|
|
/* mesh face */ |
5365 |
|
|
if(model->numface && face && !(flags & M3D_EXP_NOFACE)) { |
5366 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(model->numface * 128) + (uintptr_t)6); |
5367 |
|
|
last = M3D_UNDEF; |
5368 |
|
|
#ifdef M3D_VERTEXMAX |
5369 |
|
|
lastp = M3D_UNDEF; |
5370 |
|
|
#endif |
5371 |
|
|
if(!(flags & M3D_EXP_NOMATERIAL)) |
5372 |
|
|
for(i = 0; i < model->numface; i++) { |
5373 |
|
|
j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : M3D_UNDEF; |
5374 |
|
|
if(j != last) { |
5375 |
|
|
last = j; |
5376 |
|
|
if(last < model->nummaterial) |
5377 |
|
|
len += (unsigned int)strlen(model->material[last].name); |
5378 |
|
|
len += 6; |
5379 |
|
|
} |
5380 |
|
|
#ifdef M3D_VERTEXMAX |
5381 |
|
|
j = face[i].data.paramid < model->numparam ? face[i].data.paramid : M3D_UNDEF; |
5382 |
|
|
if(j != lastp) { |
5383 |
|
|
lastp = j; |
5384 |
|
|
if(lastp < model->numparam) |
5385 |
|
|
len += (unsigned int)strlen(model->param[lastp].name); |
5386 |
|
|
len += 6; |
5387 |
|
|
} |
5388 |
|
|
#endif |
5389 |
|
|
} |
5390 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5391 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5392 |
|
|
ptr += sprintf(ptr, "Mesh\r\n"); |
5393 |
|
|
last = M3D_UNDEF; |
5394 |
|
|
#ifdef M3D_VERTEXMAX |
5395 |
|
|
lastp = M3D_UNDEF; |
5396 |
|
|
#endif |
5397 |
|
|
for(i = 0; i < model->numface; i++) { |
5398 |
|
|
j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : M3D_UNDEF; |
5399 |
|
|
if(!(flags & M3D_EXP_NOMATERIAL) && j != last) { |
5400 |
|
|
last = j; |
5401 |
|
|
if(last < model->nummaterial) { |
5402 |
|
|
sn = _m3d_safestr(model->material[last].name, 0); |
5403 |
|
|
if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5404 |
|
|
ptr += sprintf(ptr, "use %s\r\n", sn); |
5405 |
|
|
M3D_FREE(sn); sn = NULL; |
5406 |
|
|
} else |
5407 |
|
|
ptr += sprintf(ptr, "use\r\n"); |
5408 |
|
|
} |
5409 |
|
|
#ifdef M3D_VERTEXMAX |
5410 |
|
|
j = face[i].data.paramid < model->numparam ? face[i].data.paramid : M3D_UNDEF; |
5411 |
|
|
if(!(flags & M3D_EXP_NOVRTMAX) && j != lastp) { |
5412 |
|
|
lastp = j; |
5413 |
|
|
if(lastp < model->numparam) { |
5414 |
|
|
sn = _m3d_safestr(model->param[lastp].name, 0); |
5415 |
|
|
if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5416 |
|
|
ptr += sprintf(ptr, "par %s\r\n", sn); |
5417 |
|
|
M3D_FREE(sn); sn = NULL; |
5418 |
|
|
} else |
5419 |
|
|
ptr += sprintf(ptr, "par\r\n"); |
5420 |
|
|
} |
5421 |
|
|
#endif |
5422 |
|
|
/* hardcoded triangles. Should be repeated as many times as the number of edges in polygon */ |
5423 |
|
|
for(j = 0; j < 3; j++) { |
5424 |
|
|
ptr += sprintf(ptr, "%s%d", j?" ":"", vrtxidx[face[i].data.vertex[j]]); |
5425 |
|
|
k = l = M3D_NOTDEFINED; |
5426 |
|
|
if(!(flags & M3D_EXP_NOTXTCRD) && (face[i].data.texcoord[j] != M3D_UNDEF) && |
5427 |
|
|
(tmapidx[face[i].data.texcoord[j]] != M3D_UNDEF)) { |
5428 |
|
|
k = tmapidx[face[i].data.texcoord[j]]; |
5429 |
|
|
ptr += sprintf(ptr, "/%d", k); |
5430 |
|
|
} |
5431 |
|
|
if(!(flags & M3D_EXP_NONORMAL) && (face[i].data.normal[j] != M3D_UNDEF)) { |
5432 |
|
|
l = vrtxidx[face[i].data.normal[j]]; |
5433 |
|
|
ptr += sprintf(ptr, "%s/%d", k == M3D_NOTDEFINED? "/" : "", l); |
5434 |
|
|
} |
5435 |
|
|
#ifdef M3D_VERTEXMAX |
5436 |
|
|
if(!(flags & M3D_EXP_NOVRTMAX) && (face[i].data.vertmax[j] != M3D_UNDEF)) { |
5437 |
|
|
ptr += sprintf(ptr, "%s%s/%d", k == M3D_NOTDEFINED? "/" : "", l == M3D_NOTDEFINED? "/" : "", |
5438 |
|
|
vrtxidx[face[i].data.vertmax[j]]); |
5439 |
|
|
} |
5440 |
|
|
#endif |
5441 |
|
|
} |
5442 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5443 |
|
|
} |
5444 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5445 |
|
|
} |
5446 |
|
|
/* voxel face */ |
5447 |
|
|
if(model->numvoxtype && model->voxtype && !(flags & M3D_EXP_NOFACE)) { |
5448 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(model->numvoxtype * 128) + (uintptr_t)10); |
5449 |
|
|
for(i = 0; i < model->numvoxtype; i++) { |
5450 |
|
|
if(model->voxtype[i].name) len += (unsigned int)strlen(model->voxtype[i].name); |
5451 |
|
|
for(j = 0; j < model->voxtype[i].numitem; j++) |
5452 |
|
|
if(model->voxtype[i].item[j].name) |
5453 |
|
|
len += (unsigned int)strlen(model->voxtype[i].item[j].name) + 6; |
5454 |
|
|
} |
5455 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5456 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5457 |
|
|
ptr += sprintf(ptr, "VoxTypes\r\n"); |
5458 |
|
|
for(i = 0; i < model->numvoxtype; i++) { |
5459 |
|
|
ptr += sprintf(ptr, "#%08x", model->voxtype[i].color); |
5460 |
|
|
if(model->voxtype[i].rotation) |
5461 |
|
|
ptr += sprintf(ptr, "/%02x", model->voxtype[i].rotation); |
5462 |
|
|
if(model->voxtype[i].voxshape) |
5463 |
|
|
ptr += sprintf(ptr, "%s/%03x", model->voxtype[i].rotation ? "" : "/", model->voxtype[i].voxshape); |
5464 |
|
|
sn = _m3d_safestr(model->voxtype[i].name, 0); |
5465 |
|
|
if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5466 |
|
|
ptr += sprintf(ptr, " %s", sn && sn[0] ? sn : "-"); |
5467 |
|
|
M3D_FREE(sn); sn = NULL; |
5468 |
|
|
if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin && model->voxtype[i].skinid < M3D_INDEXMAX) { |
5469 |
|
|
if(skin[skinidx[model->voxtype[i].skinid]].data.weight[0] == (M3D_FLOAT)1.0) |
5470 |
|
|
ptr += sprintf(ptr, " %d", skin[skinidx[model->voxtype[i].skinid]].data.boneid[0]); |
5471 |
|
|
else |
5472 |
|
|
for(j = 0; j < M3D_NUMBONE && skin[skinidx[model->voxtype[i].skinid]].data.boneid[j] != M3D_UNDEF && |
5473 |
|
|
skin[skinidx[model->voxtype[i].skinid]].data.weight[j] > (M3D_FLOAT)0.0; j++) |
5474 |
|
|
ptr += sprintf(ptr, " %d:%g", skin[skinidx[model->voxtype[i].skinid]].data.boneid[j], |
5475 |
|
|
skin[skinidx[model->voxtype[i].skinid]].data.weight[j]); |
5476 |
|
|
} |
5477 |
|
|
if(model->voxtype[i].numitem && model->voxtype[i].item) { |
5478 |
|
|
for(j = k = 0; j < model->voxtype[i].numitem; j++) { |
5479 |
|
|
if(!model->voxtype[i].item[j].count || !model->voxtype[i].item[j].name || |
5480 |
|
|
!model->voxtype[i].item[j].name[0]) continue; |
5481 |
|
|
if(!k) { ptr += sprintf(ptr, " {"); k = 1; } |
5482 |
|
|
sn = _m3d_safestr(model->voxtype[i].item[j].name, 0); |
5483 |
|
|
if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5484 |
|
|
ptr += sprintf(ptr, " %d %s", model->voxtype[i].item[j].count, sn); |
5485 |
|
|
M3D_FREE(sn); sn = NULL; |
5486 |
|
|
} |
5487 |
|
|
if(k) ptr += sprintf(ptr, " }"); |
5488 |
|
|
} |
5489 |
|
|
while(ptr[-1] == '-' || ptr[-1] == ' ') ptr--; |
5490 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5491 |
|
|
} |
5492 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5493 |
|
|
} |
5494 |
|
|
if(model->numvoxel && model->voxel && !(flags & M3D_EXP_NOFACE)) { |
5495 |
|
|
for(i = 0; i < model->numvoxel; i++) { |
5496 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)128); |
5497 |
|
|
if(model->voxel[i].name) len += (unsigned int)strlen(model->voxel[i].name); |
5498 |
|
|
len += model->voxel[i].h * ((model->voxel[i].w * 6 + 2) * model->voxel[i].d + 9); |
5499 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5500 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5501 |
|
|
ptr += sprintf(ptr, "Voxel"); |
5502 |
|
|
sn = _m3d_safestr(model->voxel[i].name, 0); |
5503 |
|
|
if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5504 |
|
|
if(sn && sn[0]) |
5505 |
|
|
ptr += sprintf(ptr, " %s", sn); |
5506 |
|
|
M3D_FREE(sn); sn = NULL; |
5507 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5508 |
|
|
if(model->voxel[i].uncertain) |
5509 |
|
|
ptr += sprintf(ptr, "uncertain %d %d\r\n", (model->voxel[i].uncertain * 100) / 255, model->voxel[i].groupid); |
5510 |
|
|
if(model->voxel[i].x || model->voxel[i].y || model->voxel[i].z) |
5511 |
|
|
ptr += sprintf(ptr, "pos %d %d %d\r\n", model->voxel[i].x, model->voxel[i].y, model->voxel[i].z); |
5512 |
|
|
ptr += sprintf(ptr, "dim %d %d %d\r\n", model->voxel[i].w, model->voxel[i].h, model->voxel[i].d); |
5513 |
|
|
for(j = n = 0; j < model->voxel[i].h; j++) { |
5514 |
|
|
ptr += sprintf(ptr, "layer\r\n"); |
5515 |
|
|
for(k = 0; k < model->voxel[i].d; k++) { |
5516 |
|
|
for(l = 0; l < model->voxel[i].w; l++, n++) { |
5517 |
|
|
switch(model->voxel[i].data[n]) { |
5518 |
|
|
case M3D_VOXCLEAR: *ptr++ = '-'; break; |
5519 |
|
|
case M3D_VOXUNDEF: *ptr++ = '.'; break; |
5520 |
|
|
default: ptr += sprintf(ptr, "%d", model->voxel[i].data[n]); break; |
5521 |
|
|
} |
5522 |
|
|
*ptr++ = ' '; |
5523 |
|
|
} |
5524 |
|
|
ptr--; |
5525 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5526 |
|
|
} |
5527 |
|
|
} |
5528 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5529 |
|
|
} |
5530 |
|
|
} |
5531 |
|
|
/* mathematical shapes face */ |
5532 |
|
|
if(model->numshape && model->numshape && !(flags & M3D_EXP_NOFACE)) { |
5533 |
|
|
for(j = 0; j < model->numshape; j++) { |
5534 |
|
|
sn = _m3d_safestr(model->shape[j].name, 0); |
5535 |
|
|
if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5536 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)33); |
5537 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5538 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5539 |
|
|
ptr += sprintf(ptr, "Shape %s\r\n", sn); |
5540 |
|
|
M3D_FREE(sn); sn = NULL; |
5541 |
|
|
if(model->shape[j].group != M3D_UNDEF && !(flags & M3D_EXP_NOBONE)) |
5542 |
|
|
ptr += sprintf(ptr, "group %d\r\n", model->shape[j].group); |
5543 |
|
|
for(i = 0; i < model->shape[j].numcmd; i++) { |
5544 |
|
|
cmd = &model->shape[j].cmd[i]; |
5545 |
|
|
if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg) |
5546 |
|
|
continue; |
5547 |
|
|
cd = &m3d_commandtypes[cmd->type]; |
5548 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(cd->key) + (uintptr_t)3); |
5549 |
|
|
for(k = 0; k < cd->p; k++) |
5550 |
|
|
switch(cd->a[k]) { |
5551 |
|
|
case m3dcp_mi_t: if(cmd->arg[k] != M3D_NOTDEFINED) { len += (unsigned int)strlen(model->material[cmd->arg[k]].name) + 1; } break; |
5552 |
|
|
case m3dcp_va_t: len += cmd->arg[k] * (cd->p - k - 1) * 16; k = cd->p; break; |
5553 |
|
|
default: len += 16; break; |
5554 |
|
|
} |
5555 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5556 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5557 |
|
|
ptr += sprintf(ptr, "%s", cd->key); |
5558 |
|
|
for(k = n = 0, l = cd->p; k < l; k++) { |
5559 |
|
|
switch(cd->a[((k - n) % (cd->p - n)) + n]) { |
5560 |
|
|
case m3dcp_mi_t: |
5561 |
|
|
if(cmd->arg[k] != M3D_NOTDEFINED) { |
5562 |
|
|
sn = _m3d_safestr(model->material[cmd->arg[k]].name, 0); |
5563 |
|
|
if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5564 |
|
|
ptr += sprintf(ptr, " %s", sn); |
5565 |
|
|
M3D_FREE(sn); sn = NULL; |
5566 |
|
|
} |
5567 |
|
|
break; |
5568 |
|
|
case m3dcp_vc_t: ptr += sprintf(ptr, " %g", *((float*)&cmd->arg[k])); break; |
5569 |
|
|
case m3dcp_va_t: ptr += sprintf(ptr, " %d[", cmd->arg[k]); |
5570 |
|
|
n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1); |
5571 |
|
|
break; |
5572 |
|
|
default: ptr += sprintf(ptr, " %d", cmd->arg[k]); break; |
5573 |
|
|
} |
5574 |
|
|
} |
5575 |
|
|
ptr += sprintf(ptr, "%s\r\n", l > cd->p ? " ]" : ""); |
5576 |
|
|
} |
5577 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5578 |
|
|
} |
5579 |
|
|
} |
5580 |
|
|
/* annotation labels */ |
5581 |
|
|
if(model->numlabel && model->label && !(flags & M3D_EXP_NOFACE)) { |
5582 |
|
|
for(i = 0, j = 3, length = NULL; i < model->numlabel; i++) { |
5583 |
|
|
if(model->label[i].name) j += (unsigned int)strlen(model->label[i].name); |
5584 |
|
|
if(model->label[i].lang) j += (unsigned int)strlen(model->label[i].lang); |
5585 |
|
|
if(model->label[i].text) j += (unsigned int)strlen(model->label[i].text); |
5586 |
|
|
j += 40; |
5587 |
|
|
} |
5588 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)j); |
5589 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5590 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5591 |
|
|
for(i = 0; i < model->numlabel; i++) { |
5592 |
|
|
if(!i || _m3d_strcmp(sl, model->label[i].lang) || _m3d_strcmp(sn, model->label[i].name)) { |
5593 |
|
|
sl = model->label[i].lang; |
5594 |
|
|
sn = model->label[i].name; |
5595 |
|
|
sd = _m3d_safestr(sn, 0); |
5596 |
|
|
if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; } |
5597 |
|
|
if(i) ptr += sprintf(ptr, "\r\n"); |
5598 |
|
|
ptr += sprintf(ptr, "Labels %s\r\n", sd); |
5599 |
|
|
M3D_FREE(sd); sd = NULL; |
5600 |
|
|
if(model->label[i].color) |
5601 |
|
|
ptr += sprintf(ptr, "color #0x%08x\r\n", model->label[i].color); |
5602 |
|
|
if(sl && *sl) { |
5603 |
|
|
sd = _m3d_safestr(sl, 0); |
5604 |
|
|
if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; } |
5605 |
|
|
ptr += sprintf(ptr, "lang %s\r\n", sd); |
5606 |
|
|
M3D_FREE(sd); sd = NULL; |
5607 |
|
|
} |
5608 |
|
|
} |
5609 |
|
|
sd = _m3d_safestr(model->label[i].text, 2); |
5610 |
|
|
if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; } |
5611 |
|
|
ptr += sprintf(ptr, "%d %s\r\n", model->label[i].vertexid, sd); |
5612 |
|
|
M3D_FREE(sd); sd = NULL; |
5613 |
|
|
} |
5614 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5615 |
|
|
sn = sl = NULL; |
5616 |
|
|
} |
5617 |
|
|
/* actions */ |
5618 |
|
|
if(model->numaction && model->action && !(flags & M3D_EXP_NOACTION)) { |
5619 |
|
|
for(j = 0; j < model->numaction; j++) { |
5620 |
|
|
a = &model->action[j]; |
5621 |
|
|
sn = _m3d_safestr(a->name, 0); |
5622 |
|
|
if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5623 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)48); |
5624 |
|
|
for(i = 0; i < a->numframe; i++) |
5625 |
|
|
len += a->frame[i].numtransform * 128 + 8; |
5626 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5627 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5628 |
|
|
ptr += sprintf(ptr, "Action %d %s\r\n", a->durationmsec, sn); |
5629 |
|
|
M3D_FREE(sn); sn = NULL; |
5630 |
|
|
for(i = 0; i < a->numframe; i++) { |
5631 |
|
|
ptr += sprintf(ptr, "frame %d\r\n", a->frame[i].msec); |
5632 |
|
|
for(k = 0; k < a->frame[i].numtransform; k++) { |
5633 |
|
|
ptr += sprintf(ptr, "%d %d %d\r\n", a->frame[i].transform[k].boneid, |
5634 |
|
|
vrtxidx[a->frame[i].transform[k].pos], vrtxidx[a->frame[i].transform[k].ori]); |
5635 |
|
|
} |
5636 |
|
|
} |
5637 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5638 |
|
|
} |
5639 |
|
|
} |
5640 |
|
|
/* inlined assets */ |
5641 |
|
|
if(model->numinlined && model->inlined) { |
5642 |
|
|
for(i = j = 0; i < model->numinlined; i++) |
5643 |
|
|
if(model->inlined[i].name) |
5644 |
|
|
j += (unsigned int)strlen(model->inlined[i].name) + 6; |
5645 |
|
|
if(j > 0) { |
5646 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)j + (uintptr_t)16); |
5647 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5648 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5649 |
|
|
ptr += sprintf(ptr, "Assets\r\n"); |
5650 |
|
|
for(i = 0; i < model->numinlined; i++) |
5651 |
|
|
if(model->inlined[i].name) |
5652 |
|
|
ptr += sprintf(ptr, "%s%s\r\n", model->inlined[i].name, strrchr(model->inlined[i].name, '.') ? "" : ".png"); |
5653 |
|
|
ptr += sprintf(ptr, "\r\n"); |
5654 |
|
|
} |
5655 |
|
|
} |
5656 |
|
|
/* extra info */ |
5657 |
|
|
if(model->numextra && (flags & M3D_EXP_EXTRA)) { |
5658 |
|
|
for(i = 0; i < model->numextra; i++) { |
5659 |
|
|
if(model->extra[i]->length < 9) continue; |
5660 |
|
|
ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)17 + (uintptr_t)(model->extra[i]->length * 3)); |
5661 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out; |
5662 |
|
|
if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; } |
5663 |
|
|
ptr += sprintf(ptr, "Extra %c%c%c%c\r\n", |
5664 |
|
|
model->extra[i]->magic[0] > ' ' ? model->extra[i]->magic[0] : '_', |
5665 |
|
|
model->extra[i]->magic[1] > ' ' ? model->extra[i]->magic[1] : '_', |
5666 |
|
|
model->extra[i]->magic[2] > ' ' ? model->extra[i]->magic[2] : '_', |
5667 |
|
|
model->extra[i]->magic[3] > ' ' ? model->extra[i]->magic[3] : '_'); |
5668 |
|
|
for(j = 0; j < model->extra[i]->length; j++) |
5669 |
|
|
ptr += sprintf(ptr, "%02x ", *((unsigned char *)model->extra + sizeof(m3dchunk_t) + j)); |
5670 |
|
|
ptr--; |
5671 |
|
|
ptr += sprintf(ptr, "\r\n\r\n"); |
5672 |
|
|
} |
5673 |
|
|
} |
5674 |
|
|
setlocale(LC_NUMERIC, ol); |
5675 |
|
|
len = (unsigned int)((uintptr_t)ptr - (uintptr_t)out); |
5676 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len + 1); |
5677 |
|
|
if(!out) goto memerr; |
5678 |
|
|
out[len] = 0; |
5679 |
|
|
} else |
5680 |
|
|
#endif |
5681 |
|
|
{ |
5682 |
|
|
/* stricly only use LF (newline) in binary */ |
5683 |
|
|
sd = _m3d_safestr(model->desc, 3); |
5684 |
|
|
if(!sd) goto memerr; |
5685 |
|
|
/* header */ |
5686 |
|
|
h = (m3dhdr_t*)M3D_MALLOC(sizeof(m3dhdr_t) + strlen(sn) + strlen(sl) + strlen(sa) + strlen(sd) + 4); |
5687 |
|
|
if(!h) goto memerr; |
5688 |
|
|
memcpy((uint8_t*)h, "HEAD", 4); |
5689 |
|
|
h->length = sizeof(m3dhdr_t); |
5690 |
|
|
h->scale = scale; |
5691 |
|
|
i = (unsigned int)strlen(sn); memcpy((uint8_t*)h + h->length, sn, i+1); h->length += i+1; M3D_FREE(sn); |
5692 |
|
|
i = (unsigned int)strlen(sl); memcpy((uint8_t*)h + h->length, sl, i+1); h->length += i+1; M3D_FREE(sl); |
5693 |
|
|
i = (unsigned int)strlen(sa); memcpy((uint8_t*)h + h->length, sa, i+1); h->length += i+1; M3D_FREE(sa); |
5694 |
|
|
i = (unsigned int)strlen(sd); memcpy((uint8_t*)h + h->length, sd, i+1); h->length += i+1; M3D_FREE(sd); |
5695 |
|
|
sn = sl = sa = sd = NULL; |
5696 |
|
|
if(model->inlined) |
5697 |
|
|
for(i = 0; i < model->numinlined; i++) { |
5698 |
|
|
if(model->inlined[i].name && *model->inlined[i].name && model->inlined[i].length > 0) { |
5699 |
|
|
str = _m3d_addstr(str, &numstr, model->inlined[i].name); |
5700 |
|
|
if(!str) goto memerr; |
5701 |
|
|
} |
5702 |
|
|
} |
5703 |
|
|
if(str) |
5704 |
|
|
for(i = 0; i < numstr; i++) { |
5705 |
|
|
h = _m3d_addhdr(h, &str[i]); |
5706 |
|
|
if(!h) goto memerr; |
5707 |
|
|
} |
5708 |
|
|
vc_s = quality == M3D_EXP_INT8? 1 : (quality == M3D_EXP_INT16? 2 : (quality == M3D_EXP_DOUBLE? 8 : 4)); |
5709 |
|
|
vi_s = maxvrtx < 254 ? 1 : (maxvrtx < 65534 ? 2 : 4); |
5710 |
|
|
si_s = h->length - 16 < 254 ? 1 : (h->length - 16 < 65534 ? 2 : 4); |
5711 |
|
|
ci_s = !numcmap || !cmap ? 0 : (numcmap < 254 ? 1 : (numcmap < 65534 ? 2 : 4)); |
5712 |
|
|
ti_s = !maxtmap || !tmap ? 0 : (maxtmap < 254 ? 1 : (maxtmap < 65534 ? 2 : 4)); |
5713 |
|
|
bi_s = !model->numbone || !model->bone || (flags & M3D_EXP_NOBONE)? 0 : (model->numbone < 254 ? 1 : |
5714 |
|
|
(model->numbone < 65534 ? 2 : 4)); |
5715 |
|
|
nb_s = maxbone < 2 ? 1 : (maxbone == 2 ? 2 : (maxbone <= 4 ? 4 : 8)); |
5716 |
|
|
sk_s = !bi_s || !maxskin || !skin ? 0 : (maxskin < 254 ? 1 : (maxskin < 65534 ? 2 : 4)); |
5717 |
|
|
fc_s = maxt < 254 ? 1 : (maxt < 65534 ? 2 : 4); |
5718 |
|
|
hi_s = !model->numshape || !model->shape || (flags & M3D_EXP_NOFACE)? 0 : (model->numshape < 254 ? 1 : |
5719 |
|
|
(model->numshape < 65534 ? 2 : 4)); |
5720 |
|
|
fi_s = !model->numface || !model->face || (flags & M3D_EXP_NOFACE)? 0 : (model->numface < 254 ? 1 : |
5721 |
|
|
(model->numface < 65534 ? 2 : 4)); |
5722 |
|
|
vd_s = !model->numvoxel || !model->voxel || (flags & M3D_EXP_NOFACE)? 0 : (minvox >= -128 && maxvox <= 127 ? 1 : |
5723 |
|
|
(minvox >= -32768 && maxvox <= 32767 ? 2 : 4)); |
5724 |
|
|
vp_s = !model->numvoxtype || !model->voxtype || (flags & M3D_EXP_NOFACE)? 0 : (model->numvoxtype < 254 ? 1 : |
5725 |
|
|
(model->numvoxtype < 65534 ? 2 : 4)); |
5726 |
|
|
h->types = (vc_s == 8 ? (3<<0) : (vc_s == 2 ? (1<<0) : (vc_s == 1 ? (0<<0) : (2<<0)))) | |
5727 |
|
|
(vi_s == 2 ? (1<<2) : (vi_s == 1 ? (0<<2) : (2<<2))) | |
5728 |
|
|
(si_s == 2 ? (1<<4) : (si_s == 1 ? (0<<4) : (2<<4))) | |
5729 |
|
|
(ci_s == 2 ? (1<<6) : (ci_s == 1 ? (0<<6) : (ci_s == 4 ? (2<<6) : (3<<6)))) | |
5730 |
|
|
(ti_s == 2 ? (1<<8) : (ti_s == 1 ? (0<<8) : (ti_s == 4 ? (2<<8) : (3<<8)))) | |
5731 |
|
|
(bi_s == 2 ? (1<<10): (bi_s == 1 ? (0<<10): (bi_s == 4 ? (2<<10) : (3<<10)))) | |
5732 |
|
|
(nb_s == 2 ? (1<<12): (nb_s == 1 ? (0<<12): (2<<12))) | |
5733 |
|
|
(sk_s == 2 ? (1<<14): (sk_s == 1 ? (0<<14): (sk_s == 4 ? (2<<14) : (3<<14)))) | |
5734 |
|
|
(fc_s == 2 ? (1<<16): (fc_s == 1 ? (0<<16): (2<<16))) | |
5735 |
|
|
(hi_s == 2 ? (1<<18): (hi_s == 1 ? (0<<18): (hi_s == 4 ? (2<<18) : (3<<18)))) | |
5736 |
|
|
(fi_s == 2 ? (1<<20): (fi_s == 1 ? (0<<20): (fi_s == 4 ? (2<<20) : (3<<20)))) | |
5737 |
|
|
(vd_s == 2 ? (1<<22): (vd_s == 1 ? (0<<22): (vd_s == 4 ? (2<<22) : (3<<22)))) | |
5738 |
|
|
(vp_s == 2 ? (1<<24): (vp_s == 1 ? (0<<24): (vp_s == 4 ? (2<<24) : (3<<24)))); |
5739 |
|
|
len = h->length; |
5740 |
|
|
/* color map */ |
5741 |
|
|
if(numcmap && cmap && ci_s < 4 && !(flags & M3D_EXP_NOCMAP)) { |
5742 |
|
|
chunklen = 8 + numcmap * sizeof(uint32_t); |
5743 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
5744 |
|
|
if(!h) goto memerr; |
5745 |
|
|
memcpy((uint8_t*)h + len, "CMAP", 4); |
5746 |
|
|
*((uint32_t*)((uint8_t*)h + len + 4)) = chunklen; |
5747 |
|
|
memcpy((uint8_t*)h + len + 8, cmap, chunklen - 8); |
5748 |
|
|
len += chunklen; |
5749 |
|
|
} else numcmap = 0; |
5750 |
|
|
/* texture map */ |
5751 |
|
|
if(numtmap && tmap && !(flags & M3D_EXP_NOTXTCRD) && !(flags & M3D_EXP_NOFACE)) { |
5752 |
|
|
chunklen = 8 + maxtmap * vc_s * 2; |
5753 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
5754 |
|
|
if(!h) goto memerr; |
5755 |
|
|
memcpy((uint8_t*)h + len, "TMAP", 4); |
5756 |
|
|
length = (uint32_t*)((uint8_t*)h + len + 4); |
5757 |
|
|
out = (uint8_t*)h + len + 8; |
5758 |
|
|
last = M3D_UNDEF; |
5759 |
|
|
for(i = 0; i < numtmap; i++) { |
5760 |
|
|
if(tmap[i].newidx == last) continue; |
5761 |
|
|
last = tmap[i].newidx; |
5762 |
|
|
switch(vc_s) { |
5763 |
|
|
case 1: *out++ = (uint8_t)(tmap[i].data.u * 255); *out++ = (uint8_t)(tmap[i].data.v * 255); break; |
5764 |
|
|
case 2: |
5765 |
|
|
*((uint16_t*)out) = (uint16_t)(tmap[i].data.u * 65535); out += 2; |
5766 |
|
|
*((uint16_t*)out) = (uint16_t)(tmap[i].data.v * 65535); out += 2; |
5767 |
|
|
break; |
5768 |
|
|
case 4: *((float*)out) = tmap[i].data.u; out += 4; *((float*)out) = tmap[i].data.v; out += 4; break; |
5769 |
|
|
case 8: *((double*)out) = tmap[i].data.u; out += 8; *((double*)out) = tmap[i].data.v; out += 8; break; |
5770 |
|
|
} |
5771 |
|
|
} |
5772 |
|
|
*length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); |
5773 |
|
|
out = NULL; |
5774 |
|
|
len += *length; |
5775 |
|
|
} |
5776 |
|
|
/* vertex */ |
5777 |
|
|
if(numvrtx && vrtx) { |
5778 |
|
|
chunklen = 8 + maxvrtx * (ci_s + sk_s + 4 * vc_s); |
5779 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
5780 |
|
|
if(!h) goto memerr; |
5781 |
|
|
memcpy((uint8_t*)h + len, "VRTS", 4); |
5782 |
|
|
length = (uint32_t*)((uint8_t*)h + len + 4); |
5783 |
|
|
out = (uint8_t*)h + len + 8; |
5784 |
|
|
last = M3D_UNDEF; |
5785 |
|
|
for(i = 0; i < numvrtx; i++) { |
5786 |
|
|
if(vrtx[i].newidx == last) continue; |
5787 |
|
|
last = vrtx[i].newidx; |
5788 |
|
|
switch(vc_s) { |
5789 |
|
|
case 1: |
5790 |
|
|
*out++ = (int8_t)(vrtx[i].data.x * 127); |
5791 |
|
|
*out++ = (int8_t)(vrtx[i].data.y * 127); |
5792 |
|
|
*out++ = (int8_t)(vrtx[i].data.z * 127); |
5793 |
|
|
*out++ = (int8_t)(vrtx[i].data.w * 127); |
5794 |
|
|
break; |
5795 |
|
|
case 2: |
5796 |
|
|
*((int16_t*)out) = (int16_t)(vrtx[i].data.x * 32767); out += 2; |
5797 |
|
|
*((int16_t*)out) = (int16_t)(vrtx[i].data.y * 32767); out += 2; |
5798 |
|
|
*((int16_t*)out) = (int16_t)(vrtx[i].data.z * 32767); out += 2; |
5799 |
|
|
*((int16_t*)out) = (int16_t)(vrtx[i].data.w * 32767); out += 2; |
5800 |
|
|
break; |
5801 |
|
|
case 4: |
5802 |
|
|
*((float*)out) = vrtx[i].data.x; out += 4; |
5803 |
|
|
*((float*)out) = vrtx[i].data.y; out += 4; |
5804 |
|
|
*((float*)out) = vrtx[i].data.z; out += 4; |
5805 |
|
|
*((float*)out) = vrtx[i].data.w; out += 4; |
5806 |
|
|
break; |
5807 |
|
|
case 8: |
5808 |
|
|
*((double*)out) = vrtx[i].data.x; out += 8; |
5809 |
|
|
*((double*)out) = vrtx[i].data.y; out += 8; |
5810 |
|
|
*((double*)out) = vrtx[i].data.z; out += 8; |
5811 |
|
|
*((double*)out) = vrtx[i].data.w; out += 8; |
5812 |
|
|
break; |
5813 |
|
|
} |
5814 |
|
|
idx = _m3d_cmapidx(cmap, numcmap, vrtx[i].data.color); |
5815 |
|
|
switch(ci_s) { |
5816 |
|
|
case 1: *out++ = (uint8_t)(idx); break; |
5817 |
|
|
case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break; |
5818 |
|
|
case 4: *((uint32_t*)out) = vrtx[i].data.color; out += 4; break; |
5819 |
|
|
} |
5820 |
|
|
out = _m3d_addidx(out, sk_s, vrtx[i].data.skinid); |
5821 |
|
|
} |
5822 |
|
|
*length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); |
5823 |
|
|
out = NULL; |
5824 |
|
|
len += *length; |
5825 |
|
|
} |
5826 |
|
|
/* bones chunk */ |
5827 |
|
|
if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) { |
5828 |
|
|
i = 8 + bi_s + sk_s + model->numbone * (bi_s + si_s + 2*vi_s); |
5829 |
|
|
chunklen = i + numskin * nb_s * (bi_s + 1); |
5830 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
5831 |
|
|
if(!h) goto memerr; |
5832 |
|
|
memcpy((uint8_t*)h + len, "BONE", 4); |
5833 |
|
|
length = (uint32_t*)((uint8_t*)h + len + 4); |
5834 |
|
|
out = (uint8_t*)h + len + 8; |
5835 |
|
|
out = _m3d_addidx(out, bi_s, model->numbone); |
5836 |
|
|
out = _m3d_addidx(out, sk_s, maxskin); |
5837 |
|
|
for(i = 0; i < model->numbone; i++) { |
5838 |
|
|
out = _m3d_addidx(out, bi_s, model->bone[i].parent); |
5839 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->bone[i].name)); |
5840 |
|
|
out = _m3d_addidx(out, vi_s, vrtxidx[model->bone[i].pos]); |
5841 |
|
|
out = _m3d_addidx(out, vi_s, vrtxidx[model->bone[i].ori]); |
5842 |
|
|
} |
5843 |
|
|
if(numskin && skin && sk_s) { |
5844 |
|
|
last = M3D_UNDEF; |
5845 |
|
|
for(i = 0; i < numskin; i++) { |
5846 |
|
|
if(skin[i].newidx == last) continue; |
5847 |
|
|
last = skin[i].newidx; |
5848 |
|
|
memset(&weights, 0, nb_s); |
5849 |
|
|
for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != M3D_UNDEF && |
5850 |
|
|
skin[i].data.weight[j] > (M3D_FLOAT)0.0; j++) |
5851 |
|
|
weights[j] = (uint8_t)(skin[i].data.weight[j] * 255); |
5852 |
|
|
switch(nb_s) { |
5853 |
|
|
case 1: weights[0] = 255; break; |
5854 |
|
|
case 2: memcpy(out, weights, 2); out += 2; break; |
5855 |
|
|
case 4: memcpy(out, weights, 4); out += 4; break; |
5856 |
|
|
case 8: memcpy(out, weights, 8); out += 8; break; |
5857 |
|
|
} |
5858 |
|
|
for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != M3D_UNDEF && weights[j]; j++) { |
5859 |
|
|
out = _m3d_addidx(out, bi_s, skin[i].data.boneid[j]); |
5860 |
|
|
*length += bi_s; |
5861 |
|
|
} |
5862 |
|
|
} |
5863 |
|
|
} |
5864 |
|
|
*length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); |
5865 |
|
|
out = NULL; |
5866 |
|
|
len += *length; |
5867 |
|
|
} |
5868 |
|
|
/* materials */ |
5869 |
|
|
if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) { |
5870 |
|
|
for(j = 0; j < model->nummaterial; j++) { |
5871 |
|
|
if(mtrlidx[j] == M3D_UNDEF || !model->material[j].numprop || !model->material[j].prop) continue; |
5872 |
|
|
m = &model->material[j]; |
5873 |
|
|
chunklen = 12 + si_s + m->numprop * 5; |
5874 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
5875 |
|
|
if(!h) goto memerr; |
5876 |
|
|
memcpy((uint8_t*)h + len, "MTRL", 4); |
5877 |
|
|
length = (uint32_t*)((uint8_t*)h + len + 4); |
5878 |
|
|
out = (uint8_t*)h + len + 8; |
5879 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, m->name)); |
5880 |
|
|
for(i = 0; i < m->numprop; i++) { |
5881 |
|
|
if(m->prop[i].type >= 128) { |
5882 |
|
|
if(m->prop[i].value.textureid >= model->numtexture || |
5883 |
|
|
!model->texture[m->prop[i].value.textureid].name) continue; |
5884 |
|
|
k = m3dpf_map; |
5885 |
|
|
} else { |
5886 |
|
|
for(k = 256, l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) |
5887 |
|
|
if(m->prop[i].type == m3d_propertytypes[l].id) { k = m3d_propertytypes[l].format; break; } |
5888 |
|
|
} |
5889 |
|
|
if(k == 256) continue; |
5890 |
|
|
*out++ = m->prop[i].type; |
5891 |
|
|
switch(k) { |
5892 |
|
|
case m3dpf_color: |
5893 |
|
|
if(!(flags & M3D_EXP_NOCMAP)) { |
5894 |
|
|
idx = _m3d_cmapidx(cmap, numcmap, m->prop[i].value.color); |
5895 |
|
|
switch(ci_s) { |
5896 |
|
|
case 1: *out++ = (uint8_t)(idx); break; |
5897 |
|
|
case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break; |
5898 |
|
|
case 4: *((uint32_t*)out) = (uint32_t)(m->prop[i].value.color); out += 4; break; |
5899 |
|
|
} |
5900 |
|
|
} else out--; |
5901 |
|
|
break; |
5902 |
|
|
case m3dpf_uint8: *out++ = m->prop[i].value.num; break; |
5903 |
|
|
case m3dpf_uint16: *((uint16_t*)out) = m->prop[i].value.num; out += 2; break; |
5904 |
|
|
case m3dpf_uint32: *((uint32_t*)out) = m->prop[i].value.num; out += 4; break; |
5905 |
|
|
case m3dpf_float: *((float*)out) = m->prop[i].value.fnum; out += 4; break; |
5906 |
|
|
|
5907 |
|
|
case m3dpf_map: |
5908 |
|
|
idx = _m3d_stridx(str, numstr, model->texture[m->prop[i].value.textureid].name); |
5909 |
|
|
out = _m3d_addidx(out, si_s, idx); |
5910 |
|
|
break; |
5911 |
|
|
} |
5912 |
|
|
} |
5913 |
|
|
*length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); |
5914 |
|
|
len += *length; |
5915 |
|
|
out = NULL; |
5916 |
|
|
} |
5917 |
|
|
} |
5918 |
|
|
/* procedural face */ |
5919 |
|
|
if(model->numinlined && model->inlined && !(flags & M3D_EXP_NOFACE)) { |
5920 |
|
|
/* all inlined assets which are not textures should be procedural surfaces */ |
5921 |
|
|
for(j = 0; j < model->numinlined; j++) { |
5922 |
|
|
if(!model->inlined[j].name || !model->inlined[j].name[0] || model->inlined[j].length < 4 || |
5923 |
|
|
!model->inlined[j].data || (model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' && |
5924 |
|
|
model->inlined[j].data[3] == 'G')) |
5925 |
|
|
continue; |
5926 |
|
|
for(i = k = 0; i < model->numtexture; i++) { |
5927 |
|
|
if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; } |
5928 |
|
|
} |
5929 |
|
|
if(k) continue; |
5930 |
|
|
numproc++; |
5931 |
|
|
chunklen = 8 + si_s; |
5932 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
5933 |
|
|
if(!h) goto memerr; |
5934 |
|
|
memcpy((uint8_t*)h + len, "PROC", 4); |
5935 |
|
|
*((uint32_t*)((uint8_t*)h + len + 4)) = chunklen; |
5936 |
|
|
out = (uint8_t*)h + len + 8; |
5937 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->inlined[j].name)); |
5938 |
|
|
out = NULL; |
5939 |
|
|
len += chunklen; |
5940 |
|
|
} |
5941 |
|
|
} |
5942 |
|
|
/* mesh face */ |
5943 |
|
|
if(model->numface && face && !(flags & M3D_EXP_NOFACE)) { |
5944 |
|
|
chunklen = 8 + si_s + model->numface * (6 * vi_s + 3 * ti_s + si_s + 1); |
5945 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
5946 |
|
|
if(!h) goto memerr; |
5947 |
|
|
memcpy((uint8_t*)h + len, "MESH", 4); |
5948 |
|
|
length = (uint32_t*)((uint8_t*)h + len + 4); |
5949 |
|
|
out = (uint8_t*)h + len + 8; |
5950 |
|
|
last = M3D_UNDEF; |
5951 |
|
|
#ifdef M3D_VERTEXMAX |
5952 |
|
|
lastp = M3D_UNDEF; |
5953 |
|
|
#endif |
5954 |
|
|
for(i = 0; i < model->numface; i++) { |
5955 |
|
|
if(!(flags & M3D_EXP_NOMATERIAL) && face[i].data.materialid != last) { |
5956 |
|
|
last = face[i].data.materialid; |
5957 |
|
|
idx = last < model->nummaterial ? _m3d_stridx(str, numstr, model->material[last].name) : 0; |
5958 |
|
|
*out++ = 0; |
5959 |
|
|
out = _m3d_addidx(out, si_s, idx); |
5960 |
|
|
} |
5961 |
|
|
#ifdef M3D_VERTEXMAX |
5962 |
|
|
if(!(flags & M3D_EXP_NOVRTMAX) && face[i].data.paramid != lastp) { |
5963 |
|
|
lastp = face[i].data.paramid; |
5964 |
|
|
idx = lastp < model->numparam ? _m3d_stridx(str, numstr, model->param[lastp].name) : 0; |
5965 |
|
|
*out++ = 0; |
5966 |
|
|
out = _m3d_addidx(out, si_s, idx); |
5967 |
|
|
} |
5968 |
|
|
#endif |
5969 |
|
|
/* hardcoded triangles. */ |
5970 |
|
|
k = (3 << 4) | |
5971 |
|
|
(((flags & M3D_EXP_NOTXTCRD) || !ti_s || face[i].data.texcoord[0] == M3D_UNDEF || |
5972 |
|
|
face[i].data.texcoord[1] == M3D_UNDEF || face[i].data.texcoord[2] == M3D_UNDEF) ? 0 : 1) | |
5973 |
|
|
(((flags & M3D_EXP_NONORMAL) || face[i].data.normal[0] == M3D_UNDEF || |
5974 |
|
|
face[i].data.normal[1] == M3D_UNDEF || face[i].data.normal[2] == M3D_UNDEF) ? 0 : 2) |
5975 |
|
|
#ifdef M3D_VERTEXMAX |
5976 |
|
|
| (((flags & M3D_EXP_NOVRTMAX) || face[i].data.vertmax[0] == M3D_UNDEF || |
5977 |
|
|
face[i].data.vertmax[1] == M3D_UNDEF || face[i].data.vertmax[2] == M3D_UNDEF) ? 0 : 4) |
5978 |
|
|
#endif |
5979 |
|
|
; |
5980 |
|
|
*out++ = k; |
5981 |
|
|
for(j = 0; j < 3; j++) { |
5982 |
|
|
out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.vertex[j]]); |
5983 |
|
|
if(k & 1) |
5984 |
|
|
out = _m3d_addidx(out, ti_s, tmapidx[face[i].data.texcoord[j]]); |
5985 |
|
|
if(k & 2) |
5986 |
|
|
out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.normal[j]]); |
5987 |
|
|
#ifdef M3D_VERTEXMAX |
5988 |
|
|
if(k & 4) |
5989 |
|
|
out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.vertmax[j]]); |
5990 |
|
|
#endif |
5991 |
|
|
} |
5992 |
|
|
} |
5993 |
|
|
*length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); |
5994 |
|
|
len += *length; |
5995 |
|
|
out = NULL; |
5996 |
|
|
} |
5997 |
|
|
/* voxel face */ |
5998 |
|
|
if(model->numvoxtype && model->voxtype && !(flags & M3D_EXP_NOFACE)) { |
5999 |
|
|
chunklen = 8 + si_s + model->numvoxtype * (ci_s + si_s + 3 + sk_s); |
6000 |
|
|
for(i = 0; i < model->numvoxtype; i++) |
6001 |
|
|
chunklen += model->voxtype[i].numitem * (2 + si_s); |
6002 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
6003 |
|
|
if(!h) goto memerr; |
6004 |
|
|
memcpy((uint8_t*)h + len, "VOXT", 4); |
6005 |
|
|
length = (uint32_t*)((uint8_t*)h + len + 4); |
6006 |
|
|
out = (uint8_t*)h + len + 8; |
6007 |
|
|
for(i = 0; i < model->numvoxtype; i++) { |
6008 |
|
|
if(!(flags & M3D_EXP_NOCMAP)) { |
6009 |
|
|
idx = _m3d_cmapidx(cmap, numcmap, model->voxtype[i].color); |
6010 |
|
|
switch(ci_s) { |
6011 |
|
|
case 1: *out++ = (uint8_t)(idx); break; |
6012 |
|
|
case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break; |
6013 |
|
|
case 4: *((uint32_t*)out) = (uint32_t)(model->voxtype[i].color); out += 4; break; |
6014 |
|
|
} |
6015 |
|
|
} |
6016 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxtype[i].name)); |
6017 |
|
|
*out++ = (model->voxtype[i].rotation & 0xBF) | (((model->voxtype[i].voxshape >> 8) & 1) << 6); |
6018 |
|
|
*out++ = model->voxtype[i].voxshape; |
6019 |
|
|
*out++ = model->voxtype[i].numitem; |
6020 |
|
|
if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin) |
6021 |
|
|
out = _m3d_addidx(out, sk_s, skinidx[model->voxtype[i].skinid]); |
6022 |
|
|
for(j = 0; j < model->voxtype[i].numitem; j++) { |
6023 |
|
|
out = _m3d_addidx(out, 2, model->voxtype[i].item[j].count); |
6024 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxtype[i].item[j].name)); |
6025 |
|
|
} |
6026 |
|
|
} |
6027 |
|
|
*length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); |
6028 |
|
|
len += *length; |
6029 |
|
|
out = NULL; |
6030 |
|
|
} |
6031 |
|
|
if(model->numvoxel && model->voxel && !(flags & M3D_EXP_NOFACE)) { |
6032 |
|
|
for(j = 0; j < model->numvoxel; j++) { |
6033 |
|
|
chunklen = 8 + si_s + 6 * vd_s + 2 + model->voxel[j].w * model->voxel[j].h * model->voxel[j].d * 3; |
6034 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
6035 |
|
|
if(!h) goto memerr; |
6036 |
|
|
memcpy((uint8_t*)h + len, "VOXD", 4); |
6037 |
|
|
length = (uint32_t*)((uint8_t*)h + len + 4); |
6038 |
|
|
out = (uint8_t*)h + len + 8; |
6039 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxel[j].name)); |
6040 |
|
|
out = _m3d_addidx(out, vd_s, model->voxel[j].x); |
6041 |
|
|
out = _m3d_addidx(out, vd_s, model->voxel[j].y); |
6042 |
|
|
out = _m3d_addidx(out, vd_s, model->voxel[j].z); |
6043 |
|
|
out = _m3d_addidx(out, vd_s, model->voxel[j].w); |
6044 |
|
|
out = _m3d_addidx(out, vd_s, model->voxel[j].h); |
6045 |
|
|
out = _m3d_addidx(out, vd_s, model->voxel[j].d); |
6046 |
|
|
*out++ = model->voxel[j].uncertain; |
6047 |
|
|
*out++ = model->voxel[j].groupid; |
6048 |
|
|
/* RLE compress voxel data */ |
6049 |
|
|
n = model->voxel[j].w * model->voxel[j].h * model->voxel[j].d; |
6050 |
|
|
k = o = 0; out[o++] = 0; |
6051 |
|
|
for(i = 0; i < n; i++) { |
6052 |
|
|
for(l = 1; l < 128 && i + l < n && model->voxel[j].data[i] == model->voxel[j].data[i + l]; l++); |
6053 |
|
|
if(l > 1) { |
6054 |
|
|
l--; |
6055 |
|
|
if(out[k]) { out[k]--; out[o++] = 0x80 | l; } |
6056 |
|
|
else out[k] = 0x80 | l; |
6057 |
|
|
switch(vp_s) { |
6058 |
|
|
case 1: out[o++] = model->voxel[j].data[i]; break; |
6059 |
|
|
default: *((uint16_t*)(out + o)) = model->voxel[j].data[i]; o += 2; break; |
6060 |
|
|
} |
6061 |
|
|
k = o; out[o++] = 0; |
6062 |
|
|
i += l; |
6063 |
|
|
continue; |
6064 |
|
|
} |
6065 |
|
|
out[k]++; |
6066 |
|
|
switch(vp_s) { |
6067 |
|
|
case 1: out[o++] = model->voxel[j].data[i]; break; |
6068 |
|
|
default: *((uint16_t*)(out + o)) = model->voxel[j].data[i]; o += 2; break; |
6069 |
|
|
} |
6070 |
|
|
if(out[k] > 127) { out[k]--; k = o; out[o++] = 0; } |
6071 |
|
|
} |
6072 |
|
|
if(!(out[k] & 0x80)) { if(out[k]) out[k]--; else o--; } |
6073 |
|
|
*length = (uint32_t)((uintptr_t)out + (uintptr_t)o - (uintptr_t)((uint8_t*)h + len)); |
6074 |
|
|
len += *length; |
6075 |
|
|
out = NULL; |
6076 |
|
|
} |
6077 |
|
|
} |
6078 |
|
|
/* mathematical shapes face */ |
6079 |
|
|
if(model->numshape && model->shape && !(flags & M3D_EXP_NOFACE)) { |
6080 |
|
|
for(j = 0; j < model->numshape; j++) { |
6081 |
|
|
chunklen = 12 + si_s + model->shape[j].numcmd * (M3D_CMDMAXARG + 1) * 4; |
6082 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
6083 |
|
|
if(!h) goto memerr; |
6084 |
|
|
memcpy((uint8_t*)h + len, "SHPE", 4); |
6085 |
|
|
length = (uint32_t*)((uint8_t*)h + len + 4); |
6086 |
|
|
out = (uint8_t*)h + len + 8; |
6087 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->shape[j].name)); |
6088 |
|
|
out = _m3d_addidx(out, bi_s, model->shape[j].group); |
6089 |
|
|
for(i = 0; i < model->shape[j].numcmd; i++) { |
6090 |
|
|
cmd = &model->shape[j].cmd[i]; |
6091 |
|
|
if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg) |
6092 |
|
|
continue; |
6093 |
|
|
cd = &m3d_commandtypes[cmd->type]; |
6094 |
|
|
*out++ = (cmd->type & 0x7F) | (cmd->type > 127 ? 0x80 : 0); |
6095 |
|
|
if(cmd->type > 127) *out++ = (cmd->type >> 7) & 0xff; |
6096 |
|
|
for(k = n = 0, l = cd->p; k < l; k++) { |
6097 |
|
|
switch(cd->a[((k - n) % (cd->p - n)) + n]) { |
6098 |
|
|
case m3dcp_mi_t: |
6099 |
|
|
out = _m3d_addidx(out, si_s, cmd->arg[k] < model->nummaterial ? |
6100 |
|
|
_m3d_stridx(str, numstr, model->material[cmd->arg[k]].name) : 0); |
6101 |
|
|
break; |
6102 |
|
|
case m3dcp_vc_t: |
6103 |
|
|
min_x = *((float*)&cmd->arg[k]); |
6104 |
|
|
switch(vc_s) { |
6105 |
|
|
case 1: *out++ = (int8_t)(min_x * 127); break; |
6106 |
|
|
case 2: *((int16_t*)out) = (int16_t)(min_x * 32767); out += 2; break; |
6107 |
|
|
case 4: *((float*)out) = min_x; out += 4; break; |
6108 |
|
|
case 8: *((double*)out) = min_x; out += 8; break; |
6109 |
|
|
} |
6110 |
|
|
break; |
6111 |
|
|
case m3dcp_hi_t: out = _m3d_addidx(out, hi_s, cmd->arg[k]); break; |
6112 |
|
|
case m3dcp_fi_t: out = _m3d_addidx(out, fi_s, cmd->arg[k]); break; |
6113 |
|
|
case m3dcp_ti_t: out = _m3d_addidx(out, ti_s, cmd->arg[k]); break; |
6114 |
|
|
case m3dcp_qi_t: |
6115 |
|
|
case m3dcp_vi_t: out = _m3d_addidx(out, vi_s, cmd->arg[k]); break; |
6116 |
|
|
case m3dcp_i1_t: out = _m3d_addidx(out, 1, cmd->arg[k]); break; |
6117 |
|
|
case m3dcp_i2_t: out = _m3d_addidx(out, 2, cmd->arg[k]); break; |
6118 |
|
|
case m3dcp_i4_t: out = _m3d_addidx(out, 4, cmd->arg[k]); break; |
6119 |
|
|
case m3dcp_va_t: out = _m3d_addidx(out, 4, cmd->arg[k]); |
6120 |
|
|
n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1); |
6121 |
|
|
break; |
6122 |
|
|
} |
6123 |
|
|
} |
6124 |
|
|
} |
6125 |
|
|
*length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); |
6126 |
|
|
len += *length; |
6127 |
|
|
out = NULL; |
6128 |
|
|
} |
6129 |
|
|
} |
6130 |
|
|
/* annotation labels */ |
6131 |
|
|
if(model->numlabel && model->label) { |
6132 |
|
|
for(i = 0, length = NULL; i < model->numlabel; i++) { |
6133 |
|
|
if(!i || _m3d_strcmp(sl, model->label[i].lang) || _m3d_strcmp(sn, model->label[i].name)) { |
6134 |
|
|
sl = model->label[i].lang; |
6135 |
|
|
sn = model->label[i].name; |
6136 |
|
|
if(length) { |
6137 |
|
|
*length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); |
6138 |
|
|
len += *length; |
6139 |
|
|
} |
6140 |
|
|
chunklen = 8 + 2 * si_s + ci_s + model->numlabel * (vi_s + si_s); |
6141 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
6142 |
|
|
if(!h) { sn = NULL; sl = NULL; goto memerr; } |
6143 |
|
|
memcpy((uint8_t*)h + len, "LBLS", 4); |
6144 |
|
|
length = (uint32_t*)((uint8_t*)h + len + 4); |
6145 |
|
|
out = (uint8_t*)h + len + 8; |
6146 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].name)); |
6147 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].lang)); |
6148 |
|
|
idx = _m3d_cmapidx(cmap, numcmap, model->label[i].color); |
6149 |
|
|
switch(ci_s) { |
6150 |
|
|
case 1: *out++ = (uint8_t)(idx); break; |
6151 |
|
|
case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break; |
6152 |
|
|
case 4: *((uint32_t*)out) = model->label[i].color; out += 4; break; |
6153 |
|
|
} |
6154 |
|
|
} |
6155 |
|
|
out = _m3d_addidx(out, vi_s, vrtxidx[model->label[i].vertexid]); |
6156 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].text)); |
6157 |
|
|
} |
6158 |
|
|
if(length) { |
6159 |
|
|
*length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); |
6160 |
|
|
len += *length; |
6161 |
|
|
} |
6162 |
|
|
out = NULL; |
6163 |
|
|
sn = sl = NULL; |
6164 |
|
|
} |
6165 |
|
|
/* actions */ |
6166 |
|
|
if(model->numaction && model->action && model->numbone && model->bone && !(flags & M3D_EXP_NOACTION)) { |
6167 |
|
|
for(j = 0; j < model->numaction; j++) { |
6168 |
|
|
a = &model->action[j]; |
6169 |
|
|
chunklen = 14 + si_s + a->numframe * (4 + fc_s + maxt * (bi_s + 2 * vi_s)); |
6170 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
6171 |
|
|
if(!h) goto memerr; |
6172 |
|
|
memcpy((uint8_t*)h + len, "ACTN", 4); |
6173 |
|
|
length = (uint32_t*)((uint8_t*)h + len + 4); |
6174 |
|
|
out = (uint8_t*)h + len + 8; |
6175 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, a->name)); |
6176 |
|
|
*((uint16_t*)out) = (uint16_t)(a->numframe); out += 2; |
6177 |
|
|
*((uint32_t*)out) = (uint32_t)(a->durationmsec); out += 4; |
6178 |
|
|
for(i = 0; i < a->numframe; i++) { |
6179 |
|
|
*((uint32_t*)out) = (uint32_t)(a->frame[i].msec); out += 4; |
6180 |
|
|
out = _m3d_addidx(out, fc_s, a->frame[i].numtransform); |
6181 |
|
|
for(k = 0; k < a->frame[i].numtransform; k++) { |
6182 |
|
|
out = _m3d_addidx(out, bi_s, a->frame[i].transform[k].boneid); |
6183 |
|
|
out = _m3d_addidx(out, vi_s, vrtxidx[a->frame[i].transform[k].pos]); |
6184 |
|
|
out = _m3d_addidx(out, vi_s, vrtxidx[a->frame[i].transform[k].ori]); |
6185 |
|
|
} |
6186 |
|
|
} |
6187 |
|
|
*length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len)); |
6188 |
|
|
len += *length; |
6189 |
|
|
out = NULL; |
6190 |
|
|
} |
6191 |
|
|
} |
6192 |
|
|
/* inlined assets */ |
6193 |
|
|
if(model->numinlined && model->inlined && (numproc || (flags & M3D_EXP_INLINE))) { |
6194 |
|
|
for(j = 0; j < model->numinlined; j++) { |
6195 |
|
|
if(!model->inlined[j].name || !model->inlined[j].name[0] || model->inlined[j].length<4 || !model->inlined[j].data) |
6196 |
|
|
continue; |
6197 |
|
|
if(!(flags & M3D_EXP_INLINE)) { |
6198 |
|
|
if(model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' && model->inlined[j].data[3] == 'G') |
6199 |
|
|
continue; |
6200 |
|
|
for(i = k = 0; i < model->numtexture; i++) { |
6201 |
|
|
if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; } |
6202 |
|
|
} |
6203 |
|
|
if(k) continue; |
6204 |
|
|
} |
6205 |
|
|
chunklen = 8 + si_s + model->inlined[j].length; |
6206 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
6207 |
|
|
if(!h) goto memerr; |
6208 |
|
|
memcpy((uint8_t*)h + len, "ASET", 4); |
6209 |
|
|
*((uint32_t*)((uint8_t*)h + len + 4)) = chunklen; |
6210 |
|
|
out = (uint8_t*)h + len + 8; |
6211 |
|
|
out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->inlined[j].name)); |
6212 |
|
|
memcpy(out, model->inlined[j].data, model->inlined[j].length); |
6213 |
|
|
out = NULL; |
6214 |
|
|
len += chunklen; |
6215 |
|
|
} |
6216 |
|
|
} |
6217 |
|
|
/* extra chunks */ |
6218 |
|
|
if(model->numextra && model->extra && (flags & M3D_EXP_EXTRA)) { |
6219 |
|
|
for(j = 0; j < model->numextra; j++) { |
6220 |
|
|
if(!model->extra[j] || model->extra[j]->length < 8) |
6221 |
|
|
continue; |
6222 |
|
|
chunklen = model->extra[j]->length; |
6223 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen); |
6224 |
|
|
if(!h) goto memerr; |
6225 |
|
|
memcpy((uint8_t*)h + len, model->extra[j], chunklen); |
6226 |
|
|
len += chunklen; |
6227 |
|
|
} |
6228 |
|
|
} |
6229 |
|
|
/* add end chunk */ |
6230 |
|
|
h = (m3dhdr_t*)M3D_REALLOC(h, len + 4); |
6231 |
|
|
if(!h) goto memerr; |
6232 |
|
|
memcpy((uint8_t*)h + len, "OMD3", 4); |
6233 |
|
|
len += 4; |
6234 |
|
|
/* zlib compress */ |
6235 |
|
|
if(!(flags & M3D_EXP_NOZLIB)) { |
6236 |
|
|
M3D_LOG("Deflating chunks"); |
6237 |
|
|
z = stbi_zlib_compress((unsigned char *)h, len, (int*)&l, 9); |
6238 |
|
|
if(z && l > 0 && l < len) { len = l; M3D_FREE(h); h = (m3dhdr_t*)z; } |
6239 |
|
|
} |
6240 |
|
|
/* add file header at the begining */ |
6241 |
|
|
len += 8; |
6242 |
|
|
out = (unsigned char*)M3D_MALLOC(len); |
6243 |
|
|
if(!out) goto memerr; |
6244 |
|
|
memcpy(out, "3DMO", 4); |
6245 |
|
|
*((uint32_t*)(out + 4)) = len; |
6246 |
|
|
/* preview image chunk, must be the first if exists */ |
6247 |
|
|
if(model->preview.data && model->preview.length) { |
6248 |
|
|
chunklen = 8 + model->preview.length; |
6249 |
|
|
out = (unsigned char*)M3D_REALLOC(out, len + chunklen); |
6250 |
|
|
if(!out) goto memerr; |
6251 |
|
|
memcpy((uint8_t*)out + 8, "PRVW", 4); |
6252 |
|
|
*((uint32_t*)((uint8_t*)out + 8 + 4)) = chunklen; |
6253 |
|
|
memcpy((uint8_t*)out + 8 + 8, model->preview.data, model->preview.length); |
6254 |
|
|
*((uint32_t*)(out + 4)) += chunklen; |
6255 |
|
|
} else |
6256 |
|
|
chunklen = 0; |
6257 |
|
|
memcpy(out + 8 + chunklen, h, len - 8); |
6258 |
|
|
} |
6259 |
|
|
if(size) *size = out ? len : 0; |
6260 |
|
|
if(vrtxidx) M3D_FREE(vrtxidx); |
6261 |
|
|
if(mtrlidx) M3D_FREE(mtrlidx); |
6262 |
|
|
if(tmapidx) M3D_FREE(tmapidx); |
6263 |
|
|
if(skinidx) M3D_FREE(skinidx); |
6264 |
|
|
if(norm) M3D_FREE(norm); |
6265 |
|
|
if(face) M3D_FREE(face); |
6266 |
|
|
if(cmap) M3D_FREE(cmap); |
6267 |
|
|
if(tmap) M3D_FREE(tmap); |
6268 |
|
|
if(skin) M3D_FREE(skin); |
6269 |
|
|
if(str) M3D_FREE(str); |
6270 |
|
|
if(vrtx) M3D_FREE(vrtx); |
6271 |
|
|
if(h) M3D_FREE(h); |
6272 |
|
|
return out; |
6273 |
|
|
} |
6274 |
|
|
#endif |
6275 |
|
|
|
6276 |
|
|
#endif |
6277 |
|
|
|
6278 |
|
|
#ifdef __cplusplus |
6279 |
|
|
} |
6280 |
|
|
#ifdef M3D_CPPWRAPPER |
6281 |
|
|
#include <vector> |
6282 |
|
|
#include <string> |
6283 |
|
|
#include <memory> |
6284 |
|
|
|
6285 |
|
|
/*** C++ wrapper class ***/ |
6286 |
|
|
namespace M3D { |
6287 |
|
|
#ifdef M3D_IMPLEMENTATION |
6288 |
|
|
|
6289 |
|
|
class Model { |
6290 |
|
|
public: |
6291 |
|
|
m3d_t *model; |
6292 |
|
|
|
6293 |
|
|
public: |
6294 |
|
|
Model() { |
6295 |
|
|
this->model = (m3d_t*)malloc(sizeof(m3d_t)); memset(this->model, 0, sizeof(m3d_t)); |
6296 |
|
|
} |
6297 |
|
|
Model(_unused const std::string &data, _unused m3dread_t ReadFileCB, |
6298 |
|
|
_unused m3dfree_t FreeCB, _unused M3D::Model mtllib) { |
6299 |
|
|
#ifndef M3D_NOIMPORTER |
6300 |
|
|
this->model = m3d_load((unsigned char *)data.data(), ReadFileCB, FreeCB, mtllib.model); |
6301 |
|
|
#else |
6302 |
|
|
Model(); |
6303 |
|
|
#endif |
6304 |
|
|
} |
6305 |
|
|
Model(_unused const std::vector<unsigned char> data, _unused m3dread_t ReadFileCB, |
6306 |
|
|
_unused m3dfree_t FreeCB, _unused M3D::Model mtllib) { |
6307 |
|
|
#ifndef M3D_NOIMPORTER |
6308 |
|
|
this->model = m3d_load((unsigned char *)&data[0], ReadFileCB, FreeCB, mtllib.model); |
6309 |
|
|
#else |
6310 |
|
|
Model(); |
6311 |
|
|
#endif |
6312 |
|
|
} |
6313 |
|
|
Model(_unused const unsigned char *data, _unused m3dread_t ReadFileCB, |
6314 |
|
|
_unused m3dfree_t FreeCB, _unused M3D::Model mtllib) { |
6315 |
|
|
#ifndef M3D_NOIMPORTER |
6316 |
|
|
this->model = m3d_load((unsigned char*)data, ReadFileCB, FreeCB, mtllib.model); |
6317 |
|
|
#else |
6318 |
|
|
Model(); |
6319 |
|
|
#endif |
6320 |
|
|
} |
6321 |
|
|
~Model() { m3d_free(this->model); } |
6322 |
|
|
|
6323 |
|
|
public: |
6324 |
|
|
m3d_t *getCStruct() { return this->model; } |
6325 |
|
|
std::string getName() { return std::string(this->model->name); } |
6326 |
|
|
void setName(std::string name) { this->model->name = (char*)name.c_str(); } |
6327 |
|
|
std::string getLicense() { return std::string(this->model->license); } |
6328 |
|
|
void setLicense(std::string license) { this->model->license = (char*)license.c_str(); } |
6329 |
|
|
std::string getAuthor() { return std::string(this->model->author); } |
6330 |
|
|
void setAuthor(std::string author) { this->model->author = (char*)author.c_str(); } |
6331 |
|
|
std::string getDescription() { return std::string(this->model->desc); } |
6332 |
|
|
void setDescription(std::string desc) { this->model->desc = (char*)desc.c_str(); } |
6333 |
|
|
float getScale() { return this->model->scale; } |
6334 |
|
|
void setScale(float scale) { this->model->scale = scale; } |
6335 |
|
|
std::vector<unsigned char> getPreview() { return this->model->preview.data ? |
6336 |
|
|
std::vector<unsigned char>(this->model->preview.data, this->model->preview.data + this->model->preview.length) : |
6337 |
|
|
std::vector<unsigned char>(); } |
6338 |
|
|
std::vector<uint32_t> getColorMap() { return this->model->cmap ? std::vector<uint32_t>(this->model->cmap, |
6339 |
|
|
this->model->cmap + this->model->numcmap) : std::vector<uint32_t>(); } |
6340 |
|
|
std::vector<m3dti_t> getTextureMap() { return this->model->tmap ? std::vector<m3dti_t>(this->model->tmap, |
6341 |
|
|
this->model->tmap + this->model->numtmap) : std::vector<m3dti_t>(); } |
6342 |
|
|
std::vector<m3dtx_t> getTextures() { return this->model->texture ? std::vector<m3dtx_t>(this->model->texture, |
6343 |
|
|
this->model->texture + this->model->numtexture) : std::vector<m3dtx_t>(); } |
6344 |
|
|
std::string getTextureName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numtexture ? |
6345 |
|
|
std::string(this->model->texture[idx].name) : nullptr; } |
6346 |
|
|
std::vector<m3db_t> getBones() { return this->model->bone ? std::vector<m3db_t>(this->model->bone, this->model->bone + |
6347 |
|
|
this->model->numbone) : std::vector<m3db_t>(); } |
6348 |
|
|
std::string getBoneName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numbone ? |
6349 |
|
|
std::string(this->model->bone[idx].name) : nullptr; } |
6350 |
|
|
std::vector<m3dm_t> getMaterials() { return this->model->material ? std::vector<m3dm_t>(this->model->material, |
6351 |
|
|
this->model->material + this->model->nummaterial) : std::vector<m3dm_t>(); } |
6352 |
|
|
std::string getMaterialName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->nummaterial ? |
6353 |
|
|
std::string(this->model->material[idx].name) : nullptr; } |
6354 |
|
|
int getMaterialPropertyInt(int idx, int type) { |
6355 |
|
|
if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 0 || type >= 127 || |
6356 |
|
|
!this->model->material[idx].prop) return -1; |
6357 |
|
|
for (int i = 0; i < this->model->material[idx].numprop; i++) { |
6358 |
|
|
if (this->model->material[idx].prop[i].type == type) |
6359 |
|
|
return this->model->material[idx].prop[i].value.num; |
6360 |
|
|
} |
6361 |
|
|
return -1; |
6362 |
|
|
} |
6363 |
|
|
uint32_t getMaterialPropertyColor(int idx, int type) { return this->getMaterialPropertyInt(idx, type); } |
6364 |
|
|
float getMaterialPropertyFloat(int idx, int type) { |
6365 |
|
|
if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 0 || type >= 127 || |
6366 |
|
|
!this->model->material[idx].prop) return -1.0f; |
6367 |
|
|
for (int i = 0; i < this->model->material[idx].numprop; i++) { |
6368 |
|
|
if (this->model->material[idx].prop[i].type == type) |
6369 |
|
|
return this->model->material[idx].prop[i].value.fnum; |
6370 |
|
|
} |
6371 |
|
|
return -1.0f; |
6372 |
|
|
} |
6373 |
|
|
m3dtx_t* getMaterialPropertyMap(int idx, int type) { |
6374 |
|
|
if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 128 || type > 255 || |
6375 |
|
|
!this->model->material[idx].prop) return nullptr; |
6376 |
|
|
for (int i = 0; i < this->model->material[idx].numprop; i++) { |
6377 |
|
|
if (this->model->material[idx].prop[i].type == type) |
6378 |
|
|
return this->model->material[idx].prop[i].value.textureid < this->model->numtexture ? |
6379 |
|
|
&this->model->texture[this->model->material[idx].prop[i].value.textureid] : nullptr; |
6380 |
|
|
} |
6381 |
|
|
return nullptr; |
6382 |
|
|
} |
6383 |
|
|
std::vector<m3dv_t> getVertices() { return this->model->vertex ? std::vector<m3dv_t>(this->model->vertex, |
6384 |
|
|
this->model->vertex + this->model->numvertex) : std::vector<m3dv_t>(); } |
6385 |
|
|
std::vector<m3df_t> getFace() { return this->model->face ? std::vector<m3df_t>(this->model->face, this->model->face + |
6386 |
|
|
this->model->numface) : std::vector<m3df_t>(); } |
6387 |
|
|
std::vector<m3dvt_t> getVoxelTypes() { return this->model->voxtype ? std::vector<m3dvt_t>(this->model->voxtype, |
6388 |
|
|
this->model->voxtype + this->model->numvoxtype) : std::vector<m3dvt_t>(); } |
6389 |
|
|
std::string getVoxelTypeName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxtype && |
6390 |
|
|
this->model->voxtype[idx].name && this->model->voxtype[idx].name[0] ? |
6391 |
|
|
std::string(this->model->voxtype[idx].name) : nullptr; } |
6392 |
|
|
std::vector<m3dvi_t> getVoxelTypeItems(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxtype && |
6393 |
|
|
this->model->voxtype[idx].item ? std::vector<m3dvi_t>(this->model->voxtype[idx].item, |
6394 |
|
|
this->model->voxtype[idx].item + this->model->voxtype[idx].numitem) : std::vector<m3dvi_t>(); } |
6395 |
|
|
std::vector<m3dvx_t> getVoxelBlocks() { return this->model->voxel ? std::vector<m3dvx_t>(this->model->voxel, |
6396 |
|
|
this->model->voxel + this->model->numvoxel) : std::vector<m3dvx_t>(); } |
6397 |
|
|
std::string getVoxelBlockName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxel && |
6398 |
|
|
this->model->voxel[idx].name && this->model->voxel[idx].name[0] ? |
6399 |
|
|
std::string(this->model->voxel[idx].name) : nullptr; } |
6400 |
|
|
std::vector<M3D_VOXEL> getVoxelBlockData(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxel && |
6401 |
|
|
this->model->voxel[idx].data ? std::vector<M3D_VOXEL>(this->model->voxel[idx].data, |
6402 |
|
|
this->model->voxel[idx].data + this->model->voxel[idx].w*this->model->voxel[idx].h*this->model->voxel[idx].d) : |
6403 |
|
|
std::vector<M3D_VOXEL>(); } |
6404 |
|
|
std::vector<m3dh_t> getShape() { return this->model->shape ? std::vector<m3dh_t>(this->model->shape, |
6405 |
|
|
this->model->shape + this->model->numshape) : std::vector<m3dh_t>(); } |
6406 |
|
|
std::string getShapeName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape && |
6407 |
|
|
this->model->shape[idx].name && this->model->shape[idx].name[0] ? |
6408 |
|
|
std::string(this->model->shape[idx].name) : nullptr; } |
6409 |
|
|
unsigned int getShapeGroup(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape ? |
6410 |
|
|
this->model->shape[idx].group : 0xFFFFFFFF; } |
6411 |
|
|
std::vector<m3dc_t> getShapeCommands(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape && |
6412 |
|
|
this->model->shape[idx].cmd ? std::vector<m3dc_t>(this->model->shape[idx].cmd, this->model->shape[idx].cmd + |
6413 |
|
|
this->model->shape[idx].numcmd) : std::vector<m3dc_t>(); } |
6414 |
|
|
std::vector<m3dl_t> getAnnotationLabels() { return this->model->label ? std::vector<m3dl_t>(this->model->label, |
6415 |
|
|
this->model->label + this->model->numlabel) : std::vector<m3dl_t>(); } |
6416 |
|
|
std::vector<m3ds_t> getSkin() { return this->model->skin ? std::vector<m3ds_t>(this->model->skin, this->model->skin + |
6417 |
|
|
this->model->numskin) : std::vector<m3ds_t>(); } |
6418 |
|
|
std::vector<m3da_t> getActions() { return this->model->action ? std::vector<m3da_t>(this->model->action, |
6419 |
|
|
this->model->action + this->model->numaction) : std::vector<m3da_t>(); } |
6420 |
|
|
std::string getActionName(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ? |
6421 |
|
|
std::string(this->model->action[aidx].name) : nullptr; } |
6422 |
|
|
unsigned int getActionDuration(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ? |
6423 |
|
|
this->model->action[aidx].durationmsec : 0; } |
6424 |
|
|
std::vector<m3dfr_t> getActionFrames(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ? |
6425 |
|
|
std::vector<m3dfr_t>(this->model->action[aidx].frame, this->model->action[aidx].frame + |
6426 |
|
|
this->model->action[aidx].numframe) : std::vector<m3dfr_t>(); } |
6427 |
|
|
unsigned int getActionFrameTimestamp(int aidx, int fidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction? |
6428 |
|
|
(fidx >= 0 && (unsigned int)fidx < this->model->action[aidx].numframe ? |
6429 |
|
|
this->model->action[aidx].frame[fidx].msec : 0) : 0; } |
6430 |
|
|
std::vector<m3dtr_t> getActionFrameTransforms(int aidx, int fidx) { |
6431 |
|
|
return aidx >= 0 && (unsigned int)aidx < this->model->numaction ? ( |
6432 |
|
|
fidx >= 0 && (unsigned int)fidx < this->model->action[aidx].numframe ? |
6433 |
|
|
std::vector<m3dtr_t>(this->model->action[aidx].frame[fidx].transform, |
6434 |
|
|
this->model->action[aidx].frame[fidx].transform + this->model->action[aidx].frame[fidx].numtransform) : |
6435 |
|
|
std::vector<m3dtr_t>()) : std::vector<m3dtr_t>(); } |
6436 |
|
|
std::vector<m3dtr_t> getActionFrame(int aidx, int fidx, std::vector<m3dtr_t> skeleton) { |
6437 |
|
|
m3dtr_t *pose = m3d_frame(this->model, (unsigned int)aidx, (unsigned int)fidx, |
6438 |
|
|
skeleton.size() ? &skeleton[0] : nullptr); |
6439 |
|
|
return std::vector<m3dtr_t>(pose, pose + this->model->numbone); } |
6440 |
|
|
std::vector<m3db_t> getActionPose(int aidx, unsigned int msec) { |
6441 |
|
|
m3db_t *pose = m3d_pose(this->model, (unsigned int)aidx, (unsigned int)msec); |
6442 |
|
|
return std::vector<m3db_t>(pose, pose + this->model->numbone); } |
6443 |
|
|
std::vector<m3di_t> getInlinedAssets() { return this->model->inlined ? std::vector<m3di_t>(this->model->inlined, |
6444 |
|
|
this->model->inlined + this->model->numinlined) : std::vector<m3di_t>(); } |
6445 |
|
|
std::vector<std::unique_ptr<m3dchunk_t>> getExtras() { return this->model->extra ? |
6446 |
|
|
std::vector<std::unique_ptr<m3dchunk_t>>(this->model->extra, |
6447 |
|
|
this->model->extra + this->model->numextra) : std::vector<std::unique_ptr<m3dchunk_t>>(); } |
6448 |
|
|
std::vector<unsigned char> Save(_unused int quality, _unused int flags) { |
6449 |
|
|
#ifdef M3D_EXPORTER |
6450 |
|
|
unsigned int size; |
6451 |
|
|
unsigned char *ptr = m3d_save(this->model, quality, flags, &size); |
6452 |
|
|
return ptr && size ? std::vector<unsigned char>(ptr, ptr + size) : std::vector<unsigned char>(); |
6453 |
|
|
#else |
6454 |
|
|
return std::vector<unsigned char>(); |
6455 |
|
|
#endif |
6456 |
|
|
} |
6457 |
|
|
}; |
6458 |
|
|
|
6459 |
|
|
#else |
6460 |
|
|
class Model { |
6461 |
|
|
private: |
6462 |
|
|
m3d_t *model; |
6463 |
|
|
|
6464 |
|
|
public: |
6465 |
|
|
Model(const std::string &data, m3dread_t ReadFileCB, m3dfree_t FreeCB); |
6466 |
|
|
Model(const std::vector<unsigned char> data, m3dread_t ReadFileCB, m3dfree_t FreeCB); |
6467 |
|
|
Model(const unsigned char *data, m3dread_t ReadFileCB, m3dfree_t FreeCB); |
6468 |
|
|
Model(); |
6469 |
|
|
~Model(); |
6470 |
|
|
|
6471 |
|
|
public: |
6472 |
|
|
m3d_t *getCStruct(); |
6473 |
|
|
std::string getName(); |
6474 |
|
|
void setName(std::string name); |
6475 |
|
|
std::string getLicense(); |
6476 |
|
|
void setLicense(std::string license); |
6477 |
|
|
std::string getAuthor(); |
6478 |
|
|
void setAuthor(std::string author); |
6479 |
|
|
std::string getDescription(); |
6480 |
|
|
void setDescription(std::string desc); |
6481 |
|
|
float getScale(); |
6482 |
|
|
void setScale(float scale); |
6483 |
|
|
std::vector<unsigned char> getPreview(); |
6484 |
|
|
std::vector<uint32_t> getColorMap(); |
6485 |
|
|
std::vector<m3dti_t> getTextureMap(); |
6486 |
|
|
std::vector<m3dtx_t> getTextures(); |
6487 |
|
|
std::string getTextureName(int idx); |
6488 |
|
|
std::vector<m3db_t> getBones(); |
6489 |
|
|
std::string getBoneName(int idx); |
6490 |
|
|
std::vector<m3dm_t> getMaterials(); |
6491 |
|
|
std::string getMaterialName(int idx); |
6492 |
|
|
int getMaterialPropertyInt(int idx, int type); |
6493 |
|
|
uint32_t getMaterialPropertyColor(int idx, int type); |
6494 |
|
|
float getMaterialPropertyFloat(int idx, int type); |
6495 |
|
|
m3dtx_t* getMaterialPropertyMap(int idx, int type); |
6496 |
|
|
std::vector<m3dv_t> getVertices(); |
6497 |
|
|
std::vector<m3df_t> getFace(); |
6498 |
|
|
std::vector<m3dvt_t> getVoxelTypes(); |
6499 |
|
|
std::string getVoxelTypeName(int idx); |
6500 |
|
|
std::vector<m3dvi_t> getVoxelTypeItems(int idx); |
6501 |
|
|
std::vector<m3dvx_t> getVoxelBlocks(); |
6502 |
|
|
std::string getVoxelBlockName(int idx); |
6503 |
|
|
std::vector<M3D_VOXEL> getVoxelBlockData(int idx); |
6504 |
|
|
std::vector<m3dh_t> getShape(); |
6505 |
|
|
std::string getShapeName(int idx); |
6506 |
|
|
unsigned int getShapeGroup(int idx); |
6507 |
|
|
std::vector<m3dc_t> getShapeCommands(int idx); |
6508 |
|
|
std::vector<m3dl_t> getAnnotationLabels(); |
6509 |
|
|
std::vector<m3ds_t> getSkin(); |
6510 |
|
|
std::vector<m3da_t> getActions(); |
6511 |
|
|
std::string getActionName(int aidx); |
6512 |
|
|
unsigned int getActionDuration(int aidx); |
6513 |
|
|
std::vector<m3dfr_t> getActionFrames(int aidx); |
6514 |
|
|
unsigned int getActionFrameTimestamp(int aidx, int fidx); |
6515 |
|
|
std::vector<m3dtr_t> getActionFrameTransforms(int aidx, int fidx); |
6516 |
|
|
std::vector<m3dtr_t> getActionFrame(int aidx, int fidx, std::vector<m3dtr_t> skeleton); |
6517 |
|
|
std::vector<m3db_t> getActionPose(int aidx, unsigned int msec); |
6518 |
|
|
std::vector<m3di_t> getInlinedAssets(); |
6519 |
|
|
std::vector<std::unique_ptr<m3dchunk_t>> getExtras(); |
6520 |
|
|
std::vector<unsigned char> Save(int quality, int flags); |
6521 |
|
|
}; |
6522 |
|
|
|
6523 |
|
|
#endif /* impl */ |
6524 |
|
|
} |
6525 |
|
|
#endif |
6526 |
|
|
|
6527 |
|
|
#endif /* __cplusplus */ |
6528 |
|
|
|
6529 |
|
|
#endif |
6530 |
|
|
|