GCC Code Coverage Report


Directory: ./
File: submodules/raylib/src/external/m3d.h
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 1564 0.0%
Branches: 0 2004 0.0%

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