minesweeper

A minewseeper implementation to play around with Hare and Raylib
git clone https://git.tronto.net/minesweeper
Download | Log | Files | Refs | README | LICENSE

m3d.h (305622B)


      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 64
     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     /* failsafe */
   2176     if(!fn || !*fn) return M3D_UNDEF;
   2177     /* do we have loaded this texture already? */
   2178     for(i = 0; i < model->numtexture; i++)
   2179         if(!strcmp(fn, model->texture[i].name)) return i;
   2180     /* see if it's inlined in the model */
   2181     if(model->inlined) {
   2182         for(i = 0; i < model->numinlined; i++)
   2183             if(!strcmp(fn, model->inlined[i].name)) {
   2184                 buff = model->inlined[i].data;
   2185                 len = model->inlined[i].length;
   2186                 freecb = NULL;
   2187                 break;
   2188             }
   2189     }
   2190     /* try to load from external source */
   2191     if(!buff && readfilecb) {
   2192         i = (unsigned int)strlen(fn);
   2193         if(i < 5 || fn[i - 4] != '.') {
   2194             fn2 = (char*)M3D_MALLOC(i + 5);
   2195             if(!fn2) { model->errcode = M3D_ERR_ALLOC; return M3D_UNDEF; }
   2196             memcpy(fn2, fn, i);
   2197             memcpy(fn2+i, ".png", 5);
   2198             buff = (*readfilecb)(fn2, &len);
   2199             M3D_FREE(fn2);
   2200         }
   2201         if(!buff) {
   2202             buff = (*readfilecb)(fn, &len);
   2203             if(!buff) return M3D_UNDEF;
   2204         }
   2205     }
   2206     /* add to textures array */
   2207     i = model->numtexture++;
   2208     model->texture = (m3dtx_t*)M3D_REALLOC(model->texture, model->numtexture * sizeof(m3dtx_t));
   2209     if(!model->texture) {
   2210         if(buff && freecb) (*freecb)(buff);
   2211         model->errcode = M3D_ERR_ALLOC;
   2212         return M3D_UNDEF;
   2213     }
   2214     model->texture[i].name = fn;
   2215     model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL;
   2216     if(buff) {
   2217         if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') {
   2218             s.read_from_callbacks = 0;
   2219             s.img_buffer = s.img_buffer_original = (unsigned char *) buff;
   2220             s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len;
   2221             /* don't use model->texture[i].w directly, it's a uint16_t */
   2222             w = h = len = 0;
   2223             ri.bits_per_channel = 8;
   2224             model->texture[i].d = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&len, 0, &ri);
   2225             model->texture[i].w = w;
   2226             model->texture[i].h = h;
   2227             model->texture[i].f = (uint8_t)len;
   2228         } else {
   2229 #ifdef M3D_TX_INTERP
   2230             if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) {
   2231                 M3D_LOG("Unable to generate texture");
   2232                 M3D_LOG(fn);
   2233             }
   2234 #else
   2235             M3D_LOG("Unimplemented interpreter");
   2236             M3D_LOG(fn);
   2237 #endif
   2238         }
   2239         if(freecb) (*freecb)(buff);
   2240     }
   2241     if(!model->texture[i].d)
   2242         model->errcode = M3D_ERR_UNKIMG;
   2243     return i;
   2244 }
   2245 
   2246 /* helper function to load and generate a procedural surface */
   2247 void _m3d_getpr(m3d_t *model, _unused m3dread_t readfilecb, _unused  m3dfree_t freecb, _unused char *fn)
   2248 {
   2249 #ifdef M3D_PR_INTERP
   2250     unsigned int i, len = 0;
   2251     unsigned char *buff = readfilecb && fn && *fn ? (*readfilecb)(fn, &len) : NULL;
   2252 
   2253     if(!buff && fn && *fn && model->inlined) {
   2254         for(i = 0; i < model->numinlined; i++)
   2255             if(!strcmp(fn, model->inlined[i].name)) {
   2256                 buff = model->inlined[i].data;
   2257                 len = model->inlined[i].length;
   2258                 freecb = NULL;
   2259                 break;
   2260             }
   2261     }
   2262     if(!buff || !len || (model->errcode = M3D_PR_INTERP(fn, buff, len, model)) != M3D_SUCCESS) {
   2263         M3D_LOG("Unable to generate procedural surface");
   2264         M3D_LOG(fn);
   2265         model->errcode = M3D_ERR_UNKIMG;
   2266     }
   2267     if(freecb && buff) (*freecb)(buff);
   2268 #else
   2269     (void)readfilecb;
   2270     (void)freecb;
   2271     (void)fn;
   2272     M3D_LOG("Unimplemented interpreter");
   2273     M3D_LOG(fn);
   2274     model->errcode = M3D_ERR_UNIMPL;
   2275 #endif
   2276 }
   2277 /* helpers to read indices from data stream */
   2278 #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)
   2279 _inline static unsigned char *_m3d_getidx(unsigned char *data, char type, M3D_INDEX *idx)
   2280 {
   2281     switch(type) {
   2282         case 1: *idx = data[0] > 253 ? (int8_t)data[0] : data[0]; data++; break;
   2283         case 2: *idx = *((uint16_t*)data) > 65533 ? *((int16_t*)data) : *((uint16_t*)data); data += 2; break;
   2284         case 4: *idx = *((int32_t*)data); data += 4; break;
   2285     }
   2286     return data;
   2287 }
   2288 
   2289 #ifndef M3D_NOANIMATION
   2290 /* multiply 4 x 4 matrices. Do not use float *r[16] as argument, because some compilers misinterpret that as
   2291  * 16 pointers each pointing to a float, but we need a single pointer to 16 floats. */
   2292 void _m3d_mul(M3D_FLOAT *r, M3D_FLOAT *a, M3D_FLOAT *b)
   2293 {
   2294     r[ 0] = b[ 0] * a[ 0] + b[ 4] * a[ 1] + b[ 8] * a[ 2] + b[12] * a[ 3];
   2295     r[ 1] = b[ 1] * a[ 0] + b[ 5] * a[ 1] + b[ 9] * a[ 2] + b[13] * a[ 3];
   2296     r[ 2] = b[ 2] * a[ 0] + b[ 6] * a[ 1] + b[10] * a[ 2] + b[14] * a[ 3];
   2297     r[ 3] = b[ 3] * a[ 0] + b[ 7] * a[ 1] + b[11] * a[ 2] + b[15] * a[ 3];
   2298     r[ 4] = b[ 0] * a[ 4] + b[ 4] * a[ 5] + b[ 8] * a[ 6] + b[12] * a[ 7];
   2299     r[ 5] = b[ 1] * a[ 4] + b[ 5] * a[ 5] + b[ 9] * a[ 6] + b[13] * a[ 7];
   2300     r[ 6] = b[ 2] * a[ 4] + b[ 6] * a[ 5] + b[10] * a[ 6] + b[14] * a[ 7];
   2301     r[ 7] = b[ 3] * a[ 4] + b[ 7] * a[ 5] + b[11] * a[ 6] + b[15] * a[ 7];
   2302     r[ 8] = b[ 0] * a[ 8] + b[ 4] * a[ 9] + b[ 8] * a[10] + b[12] * a[11];
   2303     r[ 9] = b[ 1] * a[ 8] + b[ 5] * a[ 9] + b[ 9] * a[10] + b[13] * a[11];
   2304     r[10] = b[ 2] * a[ 8] + b[ 6] * a[ 9] + b[10] * a[10] + b[14] * a[11];
   2305     r[11] = b[ 3] * a[ 8] + b[ 7] * a[ 9] + b[11] * a[10] + b[15] * a[11];
   2306     r[12] = b[ 0] * a[12] + b[ 4] * a[13] + b[ 8] * a[14] + b[12] * a[15];
   2307     r[13] = b[ 1] * a[12] + b[ 5] * a[13] + b[ 9] * a[14] + b[13] * a[15];
   2308     r[14] = b[ 2] * a[12] + b[ 6] * a[13] + b[10] * a[14] + b[14] * a[15];
   2309     r[15] = b[ 3] * a[12] + b[ 7] * a[13] + b[11] * a[14] + b[15] * a[15];
   2310 }
   2311 /* calculate 4 x 4 matrix inverse */
   2312 void _m3d_inv(M3D_FLOAT *m)
   2313 {
   2314     M3D_FLOAT r[16];
   2315     M3D_FLOAT det =
   2316           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]
   2317         + 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]
   2318         - 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]
   2319         + 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]
   2320         + 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]
   2321         - 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];
   2322     if(det == (M3D_FLOAT)0.0 || det == (M3D_FLOAT)-0.0) det = (M3D_FLOAT)1.0; else det = (M3D_FLOAT)1.0 / det;
   2323     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]));
   2324     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]));
   2325     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]));
   2326     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]));
   2327     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]));
   2328     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]));
   2329     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]));
   2330     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]));
   2331     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]));
   2332     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]));
   2333     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]));
   2334     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]));
   2335     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]));
   2336     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]));
   2337     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]));
   2338     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]));
   2339     memcpy(m, &r, sizeof(r));
   2340 }
   2341 /* compose a coloumn major 4 x 4 matrix from vec3 position and vec4 orientation/rotation quaternion */
   2342 void _m3d_mat(M3D_FLOAT *r, m3dv_t *p, m3dv_t *q)
   2343 {
   2344     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 &&
   2345         q->w == (M3D_FLOAT)0.0) {
   2346         r[ 1] = r[ 2] = r[ 4] = r[ 6] = r[ 8] = r[ 9] = (M3D_FLOAT)0.0;
   2347         r[ 0] = r[ 5] = r[10] = (M3D_FLOAT)-1.0;
   2348     } else {
   2349         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;
   2350         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;
   2351         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;
   2352         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;
   2353         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;
   2354         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;
   2355         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;
   2356         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;
   2357         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;
   2358     }
   2359     r[ 3] = p->x; r[ 7] = p->y; r[11] = p->z;
   2360     r[12] = 0; r[13] = 0; r[14] = 0; r[15] = 1;
   2361 }
   2362 #endif
   2363 #if !defined(M3D_NOANIMATION) || !defined(M3D_NONORMALS)
   2364 /* portable fast inverse square root calculation. returns 1/sqrt(x) */
   2365 static M3D_FLOAT _m3d_rsq(M3D_FLOAT x)
   2366 {
   2367 #ifdef M3D_DOUBLE
   2368     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;
   2369 #else
   2370     /* John Carmack's */
   2371     float x2 = x * 0.5f;
   2372     uint32_t *i = (uint32_t*)&x;
   2373     *i = (0x5f3759df - (*i >> 1));
   2374     return x * (1.5f - (x2 * x * x));
   2375 #endif
   2376 }
   2377 #endif
   2378 
   2379 /**
   2380  * Function to decode a Model 3D into in-memory format
   2381  */
   2382 m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d_t *mtllib)
   2383 {
   2384     unsigned char *end, *chunk, *buff, weights[8];
   2385     unsigned int i, j, k, l, n, am, len = 0, reclen, offs;
   2386 #ifndef M3D_NOVOXELS
   2387     int32_t min_x, min_y, min_z, max_x, max_y, max_z, sx, sy, sz, x, y, z;
   2388     M3D_INDEX edge[8], enorm;
   2389 #endif
   2390     char *name, *lang;
   2391     float f;
   2392     m3d_t *model;
   2393     M3D_INDEX mi;
   2394 #ifdef M3D_VERTEXMAX
   2395     M3D_INDEX pi;
   2396 #endif
   2397     M3D_FLOAT w;
   2398     m3dcd_t *cd;
   2399     m3dtx_t *tx;
   2400     m3dh_t *h;
   2401     m3dm_t *m;
   2402     m3da_t *a;
   2403     m3di_t *t;
   2404 #ifndef M3D_NONORMALS
   2405     char neednorm = 0;
   2406     m3dv_t *norm = NULL, *v0, *v1, *v2, va, vb;
   2407 #endif
   2408 #ifndef M3D_NOANIMATION
   2409     M3D_FLOAT r[16];
   2410 #endif
   2411 #if !defined(M3D_NOWEIGHTS) || !defined(M3D_NOANIMATION)
   2412     m3db_t *b;
   2413 #endif
   2414 #ifndef M3D_NOWEIGHTS
   2415     m3ds_t *sk;
   2416 #endif
   2417 #ifdef M3D_ASCII
   2418     m3ds_t s;
   2419     M3D_INDEX bi[M3D_BONEMAXLEVEL+1], level;
   2420     const char *ol;
   2421     char *ptr, *pe, *fn;
   2422 #endif
   2423 #ifdef M3D_PROFILING
   2424     struct timeval tv0, tv1, tvd;
   2425     gettimeofday(&tv0, NULL);
   2426 #endif
   2427 
   2428     if(!data || (!M3D_CHUNKMAGIC(data, '3','D','M','O')
   2429 #ifdef M3D_ASCII
   2430         && !M3D_CHUNKMAGIC(data, '3','d','m','o')
   2431 #endif
   2432         )) return NULL;
   2433     model = (m3d_t*)M3D_MALLOC(sizeof(m3d_t));
   2434     if(!model) {
   2435         M3D_LOG("Out of memory");
   2436         return NULL;
   2437     }
   2438     memset(model, 0, sizeof(m3d_t));
   2439 
   2440     if(mtllib) {
   2441         model->nummaterial = mtllib->nummaterial;
   2442         model->material = mtllib->material;
   2443         model->numtexture = mtllib->numtexture;
   2444         model->texture = mtllib->texture;
   2445         model->flags |= M3D_FLG_MTLLIB;
   2446     }
   2447 #ifdef M3D_ASCII
   2448     /* ASCII variant? */
   2449     if(M3D_CHUNKMAGIC(data, '3','d','m','o')) {
   2450         model->errcode = M3D_ERR_BADFILE;
   2451         model->flags |= M3D_FLG_FREESTR;
   2452         model->raw = (m3dhdr_t*)data;
   2453         ptr = (char*)data;
   2454         ol = setlocale(LC_NUMERIC, NULL);
   2455         setlocale(LC_NUMERIC, "C");
   2456         /* parse header. Don't use sscanf, that's incredibly slow */
   2457         ptr = _m3d_findarg(ptr);
   2458         if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2459         pe = _m3d_findnl(ptr);
   2460         model->scale = (float)strtod(ptr, NULL); ptr = pe;
   2461         if(model->scale <= (M3D_FLOAT)0.0) model->scale = (M3D_FLOAT)1.0;
   2462         model->name = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr);
   2463         if(!*ptr) goto asciiend;
   2464         model->license = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr);
   2465         if(!*ptr) goto asciiend;
   2466         model->author = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr);
   2467         if(!*ptr) goto asciiend;
   2468         if(*ptr != '\r' && *ptr != '\n')
   2469             model->desc = _m3d_safestr(ptr, 3);
   2470         while(*ptr) {
   2471             while(*ptr && *ptr!='\n') ptr++;
   2472             ptr++; if(*ptr=='\r') ptr++;
   2473             if(*ptr == '\n') break;
   2474         }
   2475 
   2476         /* the main chunk reader loop */
   2477         while(*ptr) {
   2478             while(*ptr && (*ptr == '\r' || *ptr == '\n')) ptr++;
   2479             if(!*ptr || (ptr[0]=='E' && ptr[1]=='n' && ptr[2]=='d')) break;
   2480             /* make sure there's at least one data row */
   2481             pe = ptr; ptr = _m3d_findnl(ptr);
   2482             if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2483             /* Preview chunk */
   2484             if(!memcmp(pe, "Preview", 7)) {
   2485                 if(readfilecb) {
   2486                     pe = _m3d_safestr(ptr, 0);
   2487                     if(!pe || !*pe) goto asciiend;
   2488                     model->preview.data = (*readfilecb)(pe, &model->preview.length);
   2489                     M3D_FREE(pe);
   2490                 }
   2491                 while(*ptr && *ptr != '\r' && *ptr != '\n')
   2492                     ptr = _m3d_findnl(ptr);
   2493             } else
   2494             /* texture map chunk */
   2495             if(!memcmp(pe, "Textmap", 7)) {
   2496                 if(model->tmap) { M3D_LOG("More texture map chunks, should be unique"); goto asciiend; }
   2497                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   2498                     i = model->numtmap++;
   2499                     model->tmap = (m3dti_t*)M3D_REALLOC(model->tmap, model->numtmap * sizeof(m3dti_t));
   2500                     if(!model->tmap) goto memerr;
   2501                     ptr = _m3d_getfloat(ptr, &model->tmap[i].u);
   2502                     if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2503                     _m3d_getfloat(ptr, &model->tmap[i].v);
   2504                     ptr = _m3d_findnl(ptr);
   2505                 }
   2506             } else
   2507             /* vertex chunk */
   2508             if(!memcmp(pe, "Vertex", 6)) {
   2509                 if(model->vertex) { M3D_LOG("More vertex chunks, should be unique"); goto asciiend; }
   2510                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   2511                     i = model->numvertex++;
   2512                     model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
   2513                     if(!model->vertex) goto memerr;
   2514                     memset(&model->vertex[i], 0, sizeof(m3dv_t));
   2515                     model->vertex[i].skinid = M3D_UNDEF;
   2516                     model->vertex[i].color = 0;
   2517                     model->vertex[i].w = (M3D_FLOAT)1.0;
   2518                     ptr = _m3d_getfloat(ptr, &model->vertex[i].x);
   2519                     if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2520                     ptr = _m3d_getfloat(ptr, &model->vertex[i].y);
   2521                     if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2522                     ptr = _m3d_getfloat(ptr, &model->vertex[i].z);
   2523                     if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2524                     ptr = _m3d_getfloat(ptr, &model->vertex[i].w);
   2525                     if(!*ptr) goto asciiend;
   2526                     if(*ptr == '#') {
   2527                         ptr = _m3d_gethex(ptr, &model->vertex[i].color);
   2528                         if(!*ptr) goto asciiend;
   2529                     }
   2530                     /* parse skin */
   2531                     memset(&s, 0, sizeof(m3ds_t));
   2532                     for(j = 0, w = (M3D_FLOAT)0.0; j < M3D_NUMBONE && *ptr && *ptr != '\r' && *ptr != '\n'; j++) {
   2533                         ptr = _m3d_findarg(ptr);
   2534                         if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2535                         ptr = _m3d_getint(ptr, &k);
   2536                         s.boneid[j] = (M3D_INDEX)k;
   2537                         if(*ptr == ':') {
   2538                             ptr++;
   2539                             ptr = _m3d_getfloat(ptr, &s.weight[j]);
   2540                             w += s.weight[j];
   2541                         } else if(!j)
   2542                             s.weight[j] = (M3D_FLOAT)1.0;
   2543                         if(!*ptr) goto asciiend;
   2544                     }
   2545                     if(s.boneid[0] != M3D_UNDEF && s.weight[0] > (M3D_FLOAT)0.0) {
   2546                         if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0)
   2547                             for(j = 0; j < M3D_NUMBONE && s.weight[j] > (M3D_FLOAT)0.0; j++)
   2548                                 s.weight[j] /= w;
   2549                         k = M3D_NOTDEFINED;
   2550                         if(model->skin) {
   2551                             for(j = 0; j < model->numskin; j++)
   2552                                 if(!memcmp(&model->skin[j], &s, sizeof(m3ds_t))) { k = j; break; }
   2553                         }
   2554                         if(k == M3D_NOTDEFINED) {
   2555                             k = model->numskin++;
   2556                             model->skin = (m3ds_t*)M3D_REALLOC(model->skin, model->numskin * sizeof(m3ds_t));
   2557                             if(!model->skin) goto memerr;
   2558                             memcpy(&model->skin[k], &s, sizeof(m3ds_t));
   2559                         }
   2560                         model->vertex[i].skinid = (M3D_INDEX)k;
   2561                     }
   2562                     ptr = _m3d_findnl(ptr);
   2563                 }
   2564             } else
   2565             /* Skeleton, bone hierarchy */
   2566             if(!memcmp(pe, "Bones", 5)) {
   2567                 if(model->bone) { M3D_LOG("More bones chunks, should be unique"); goto asciiend; }
   2568                 bi[0] = M3D_UNDEF;
   2569                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   2570                     i = model->numbone++;
   2571                     model->bone = (m3db_t*)M3D_REALLOC(model->bone, model->numbone * sizeof(m3db_t));
   2572                     if(!model->bone) goto memerr;
   2573                     for(level = 0; *ptr == '/'; ptr++, level++);
   2574                     if(level > M3D_BONEMAXLEVEL || !*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2575                     bi[level+1] = i;
   2576                     model->bone[i].numweight = 0;
   2577                     model->bone[i].weight = NULL;
   2578                     model->bone[i].parent = bi[level];
   2579                     ptr = _m3d_getint(ptr, &k);
   2580                     ptr = _m3d_findarg(ptr);
   2581                     if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2582                     model->bone[i].pos = (M3D_INDEX)k;
   2583                     ptr = _m3d_getint(ptr, &k);
   2584                     ptr = _m3d_findarg(ptr);
   2585                     if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2586                     model->bone[i].ori = (M3D_INDEX)k;
   2587                     model->vertex[k].skinid = M3D_INDEXMAX;
   2588                     pe = _m3d_safestr(ptr, 0);
   2589                     if(!pe || !*pe) goto asciiend;
   2590                     model->bone[i].name = pe;
   2591                     ptr = _m3d_findnl(ptr);
   2592                 }
   2593             } else
   2594             /* material chunk */
   2595             if(!memcmp(pe, "Material", 8)) {
   2596                 pe = _m3d_findarg(pe);
   2597                 if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
   2598                 pe = _m3d_safestr(pe, 0);
   2599                 if(!pe || !*pe) goto asciiend;
   2600                 for(i = 0; i < model->nummaterial; i++)
   2601                     if(!strcmp(pe, model->material[i].name)) {
   2602                         M3D_LOG("Multiple definitions for material");
   2603                         M3D_LOG(pe);
   2604                         M3D_FREE(pe);
   2605                         pe = NULL;
   2606                         while(*ptr && *ptr != '\r' && *ptr != '\n') ptr = _m3d_findnl(ptr);
   2607                         break;
   2608                     }
   2609                 if(!pe) continue;
   2610                 i = model->nummaterial++;
   2611                 if(model->flags & M3D_FLG_MTLLIB) {
   2612                     m = model->material;
   2613                     model->material = (m3dm_t*)M3D_MALLOC(model->nummaterial * sizeof(m3dm_t));
   2614                     if(!model->material) goto memerr;
   2615                     memcpy(model->material, m, (model->nummaterial - 1) * sizeof(m3dm_t));
   2616                     if(model->texture) {
   2617                         tx = model->texture;
   2618                         model->texture = (m3dtx_t*)M3D_MALLOC(model->numtexture * sizeof(m3dtx_t));
   2619                         if(!model->texture) goto memerr;
   2620                         memcpy(model->texture, tx, model->numtexture * sizeof(m3dm_t));
   2621                     }
   2622                     model->flags &= ~M3D_FLG_MTLLIB;
   2623                 } else {
   2624                     model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t));
   2625                     if(!model->material) goto memerr;
   2626                 }
   2627                 m = &model->material[i];
   2628                 m->name = pe;
   2629                 m->numprop = 0;
   2630                 m->prop = NULL;
   2631                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   2632                     k = n = 256;
   2633                     if(*ptr == 'm' && *(ptr+1) == 'a' && *(ptr+2) == 'p' && *(ptr+3) == '_') {
   2634                         k = m3dpf_map;
   2635                         ptr += 4;
   2636                     }
   2637                     for(j = 0; j < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); j++)
   2638                         if(!memcmp(ptr, m3d_propertytypes[j].key, strlen(m3d_propertytypes[j].key))) {
   2639                             n = m3d_propertytypes[j].id;
   2640                             if(k != m3dpf_map) k = m3d_propertytypes[j].format;
   2641                             break;
   2642                         }
   2643                     if(n != 256 && k != 256) {
   2644                         ptr = _m3d_findarg(ptr);
   2645                         if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2646                         j = m->numprop++;
   2647                         m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
   2648                         if(!m->prop) goto memerr;
   2649                         m->prop[j].type = n + (k == m3dpf_map && n < 128 ? 128 : 0);
   2650                         switch(k) {
   2651                             case m3dpf_color: ptr = _m3d_gethex(ptr, &m->prop[j].value.color); break;
   2652                             case m3dpf_uint8:
   2653                             case m3dpf_uint16:
   2654                             case m3dpf_uint32: ptr = _m3d_getint(ptr, &m->prop[j].value.num); break;
   2655                             case m3dpf_float:  ptr = _m3d_getfloat(ptr, &m->prop[j].value.fnum); break;
   2656                             case m3dpf_map:
   2657                                 pe = _m3d_safestr(ptr, 0);
   2658                                 if(!pe || !*pe) goto asciiend;
   2659                                 m->prop[j].value.textureid = _m3d_gettx(model, readfilecb, freecb, pe);
   2660                                 if(model->errcode == M3D_ERR_ALLOC) { M3D_FREE(pe); goto memerr; }
   2661                                 /* this error code only returned if readfilecb was specified */
   2662                                 if(m->prop[j].value.textureid == M3D_UNDEF) {
   2663                                     M3D_LOG("Texture not found");
   2664                                     M3D_LOG(pe);
   2665                                     m->numprop--;
   2666                                 }
   2667                                 M3D_FREE(pe);
   2668                             break;
   2669                         }
   2670                     } else {
   2671                         M3D_LOG("Unknown material property in");
   2672                         M3D_LOG(m->name);
   2673                         model->errcode = M3D_ERR_UNKPROP;
   2674                     }
   2675                     ptr = _m3d_findnl(ptr);
   2676                 }
   2677                 if(!m->numprop) model->nummaterial--;
   2678             } else
   2679             /* procedural */
   2680             if(!memcmp(pe, "Procedural", 10)) {
   2681                 pe = _m3d_safestr(ptr, 0);
   2682                 _m3d_getpr(model, readfilecb, freecb, pe);
   2683                 M3D_FREE(pe);
   2684                 while(*ptr && *ptr != '\r' && *ptr != '\n') ptr = _m3d_findnl(ptr);
   2685             } else
   2686             /* mesh */
   2687             if(!memcmp(pe, "Mesh", 4)) {
   2688                 mi = M3D_UNDEF;
   2689 #ifdef M3D_VERTEXMAX
   2690                 pi = M3D_UNDEF;
   2691 #endif
   2692                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   2693                     if(*ptr == 'u') {
   2694                         ptr = _m3d_findarg(ptr);
   2695                         if(!*ptr) goto asciiend;
   2696                         mi = M3D_UNDEF;
   2697                         if(*ptr != '\r' && *ptr != '\n') {
   2698                             pe = _m3d_safestr(ptr, 0);
   2699                             if(!pe || !*pe) goto asciiend;
   2700                             for(j = 0; j < model->nummaterial; j++)
   2701                                 if(!strcmp(pe, model->material[j].name)) { mi = (M3D_INDEX)j; break; }
   2702                             if(mi == M3D_UNDEF && !(model->flags & M3D_FLG_MTLLIB)) {
   2703                                 mi = model->nummaterial++;
   2704                                 model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t));
   2705                                 if(!model->material) goto memerr;
   2706                                 model->material[mi].name = pe;
   2707                                 model->material[mi].numprop = 1;
   2708                                 model->material[mi].prop = NULL;
   2709                             } else
   2710                                 M3D_FREE(pe);
   2711                         }
   2712                     } else
   2713                     if(*ptr == 'p') {
   2714                         ptr = _m3d_findarg(ptr);
   2715                         if(!*ptr) goto asciiend;
   2716 #ifdef M3D_VERTEXMAX
   2717                         pi = M3D_UNDEF;
   2718                         if(*ptr != '\r' && *ptr != '\n') {
   2719                             pe = _m3d_safestr(ptr, 0);
   2720                             if(!pe || !*pe) goto asciiend;
   2721                             for(j = 0; j < model->numparam; j++)
   2722                                 if(!strcmp(pe, model->param[j].name)) { pi = (M3D_INDEX)j; break; }
   2723                             if(pi == M3D_UNDEF) {
   2724                                 pi = model->numparam++;
   2725                                 model->param = (m3dvi_t*)M3D_REALLOC(model->param, model->numparam * sizeof(m3dvi_t));
   2726                                 if(!model->param) goto memerr;
   2727                                 model->param[pi].name = pe;
   2728                                 model->param[pi].count = 0;
   2729                             } else
   2730                                 M3D_FREE(pe);
   2731                         }
   2732 #endif
   2733                     } else {
   2734                         i = model->numface++;
   2735                         model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t));
   2736                         if(!model->face) goto memerr;
   2737                         memset(&model->face[i], 255, sizeof(m3df_t)); /* set all index to -1 by default */
   2738                         model->face[i].materialid = mi;
   2739 #ifdef M3D_VERTEXMAX
   2740                         model->face[i].paramid = pi;
   2741 #endif
   2742                         /* hardcoded triangles. */
   2743                         for(j = 0; j < 3; j++) {
   2744                             /* vertex */
   2745                             ptr = _m3d_getint(ptr, &k);
   2746                             model->face[i].vertex[j] = (M3D_INDEX)k;
   2747                             if(!*ptr) goto asciiend;
   2748                             if(*ptr == '/') {
   2749                                 ptr++;
   2750                                 if(*ptr != '/') {
   2751                                     /* texcoord */
   2752                                     ptr = _m3d_getint(ptr, &k);
   2753                                     model->face[i].texcoord[j] = (M3D_INDEX)k;
   2754                                     if(!*ptr) goto asciiend;
   2755                                 }
   2756                                 if(*ptr == '/') {
   2757                                     ptr++;
   2758                                     /* normal */
   2759                                     ptr = _m3d_getint(ptr, &k);
   2760                                     model->face[i].normal[j] = (M3D_INDEX)k;
   2761                                     if(!*ptr) goto asciiend;
   2762                                 }
   2763                                 if(*ptr == '/') {
   2764                                     ptr++;
   2765                                     /* maximum */
   2766                                     ptr = _m3d_getint(ptr, &k);
   2767 #ifdef M3D_VERTEXMAX
   2768                                     model->face[i].vertmax[j] = (M3D_INDEX)k;
   2769 #endif
   2770                                     if(!*ptr) goto asciiend;
   2771                                 }
   2772                             }
   2773 #ifndef M3D_NONORMALS
   2774                             if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1;
   2775 #endif
   2776                             ptr = _m3d_findarg(ptr);
   2777                         }
   2778                     }
   2779                     ptr = _m3d_findnl(ptr);
   2780                 }
   2781             } else
   2782             /* voxel types chunk */
   2783             if(!memcmp(pe, "VoxTypes", 8) || !memcmp(pe, "Voxtypes", 8)) {
   2784                 if(model->voxtype) { M3D_LOG("More voxel types chunks, should be unique"); goto asciiend; }
   2785                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   2786                     i = model->numvoxtype++;
   2787                     model->voxtype = (m3dvt_t*)M3D_REALLOC(model->voxtype, model->numvoxtype * sizeof(m3dvt_t));
   2788                     if(!model->voxtype) goto memerr;
   2789                     memset(&model->voxtype[i], 0, sizeof(m3dvt_t));
   2790                     model->voxtype[i].materialid = M3D_UNDEF;
   2791                     model->voxtype[i].skinid = M3D_UNDEF;
   2792                     ptr = _m3d_gethex(ptr, &model->voxtype[i].color);
   2793                     if(!*ptr) goto asciiend;
   2794                     if(*ptr == '/') {
   2795                         ptr = _m3d_gethex(ptr, &k);
   2796                         model->voxtype[i].rotation = k;
   2797                         if(!*ptr) goto asciiend;
   2798                         if(*ptr == '/') {
   2799                             ptr = _m3d_gethex(ptr, &k);
   2800                             model->voxtype[i].voxshape = k;
   2801                             if(!*ptr) goto asciiend;
   2802                         }
   2803                     }
   2804                     while(*ptr == ' ' || *ptr == '\t') ptr++;
   2805                     if(*ptr == '\r' || *ptr == '\n') { ptr = _m3d_findnl(ptr); continue; }
   2806                     /* name */
   2807                     if(*ptr != '-') {
   2808                         pe = _m3d_safestr(ptr, 0);
   2809                         if(!pe || !*pe) goto asciiend;
   2810                         model->voxtype[i].name = pe;
   2811                         for(j = 0; j < model->nummaterial; j++)
   2812                             if(!strcmp(pe, model->material[j].name)) { model->voxtype[i].materialid = (M3D_INDEX)j; break; }
   2813                     }
   2814                     ptr = _m3d_findarg(ptr);
   2815                     /* parse skin */
   2816                     memset(&s, 0, sizeof(m3ds_t));
   2817                     for(j = 0, w = (M3D_FLOAT)0.0; j < M3D_NUMBONE && *ptr && *ptr != '{' && *ptr != '\r' && *ptr != '\n'; j++) {
   2818                         ptr = _m3d_getint(ptr, &k);
   2819                         s.boneid[j] = (M3D_INDEX)k;
   2820                         if(*ptr == ':') {
   2821                             ptr++;
   2822                             ptr = _m3d_getfloat(ptr, &s.weight[j]);
   2823                             w += s.weight[j];
   2824                         } else if(!j)
   2825                             s.weight[j] = (M3D_FLOAT)1.0;
   2826                         if(!*ptr) goto asciiend;
   2827                         ptr = _m3d_findarg(ptr);
   2828                     }
   2829                     if(s.boneid[0] != M3D_UNDEF && s.weight[0] > (M3D_FLOAT)0.0) {
   2830                         if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0)
   2831                             for(j = 0; j < M3D_NUMBONE && s.weight[j] > (M3D_FLOAT)0.0; j++)
   2832                                 s.weight[j] /= w;
   2833                         k = M3D_NOTDEFINED;
   2834                         if(model->skin) {
   2835                             for(j = 0; j < model->numskin; j++)
   2836                                 if(!memcmp(&model->skin[j], &s, sizeof(m3ds_t))) { k = j; break; }
   2837                         }
   2838                         if(k == M3D_NOTDEFINED) {
   2839                             k = model->numskin++;
   2840                             model->skin = (m3ds_t*)M3D_REALLOC(model->skin, model->numskin * sizeof(m3ds_t));
   2841                             if(!model->skin) goto memerr;
   2842                             memcpy(&model->skin[k], &s, sizeof(m3ds_t));
   2843                         }
   2844                         model->voxtype[i].skinid = (M3D_INDEX)k;
   2845                     }
   2846                     /* parse item list */
   2847                     if(*ptr == '{') {
   2848                         while(*ptr == '{' || *ptr == ' ' || *ptr == '\t') ptr++;
   2849                         while(*ptr && *ptr != '}' && *ptr != '\r' && *ptr != '\n') {
   2850                             ptr = _m3d_getint(ptr, &k);
   2851                             ptr = _m3d_findarg(ptr);
   2852                             if(!*ptr || *ptr == '}' || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2853                             pe = _m3d_safestr(ptr, 0);
   2854                             if(!pe || !*pe) goto asciiend;
   2855                             ptr = _m3d_findarg(ptr);
   2856                             j = model->voxtype[i].numitem++;
   2857                             model->voxtype[i].item = (m3dvi_t*)M3D_REALLOC(model->voxtype[i].item,
   2858                                 model->voxtype[i].numitem * sizeof(m3dvi_t));
   2859                             if(!model->voxtype[i].item) goto memerr;
   2860                             model->voxtype[i].item[j].count = k;
   2861                             model->voxtype[i].item[j].name = pe;
   2862                         }
   2863                         if(*ptr != '}') goto asciiend;
   2864                     }
   2865                     ptr = _m3d_findnl(ptr);
   2866                 }
   2867             } else
   2868             /* voxel data */
   2869             if(!memcmp(pe, "Voxel", 5)) {
   2870                 if(!model->voxtype) { M3D_LOG("No voxel type chunk before voxel data"); goto asciiend; }
   2871                 pe = _m3d_findarg(pe);
   2872                 if(!*pe) goto asciiend;
   2873                 if(*pe == '\r' || *pe == '\n') pe = NULL;
   2874                 else pe = _m3d_safestr(pe, 0);
   2875                 i = model->numvoxel++;
   2876                 model->voxel = (m3dvx_t*)M3D_REALLOC(model->voxel, model->numvoxel * sizeof(m3dvx_t));
   2877                 if(!model->voxel) goto memerr;
   2878                 memset(&model->voxel[i], 0, sizeof(m3dvx_t));
   2879                 model->voxel[i].name = pe;
   2880                 k = l = 0;
   2881                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   2882                     switch(*ptr) {
   2883                         case 'u':
   2884                             ptr = _m3d_findarg(ptr);
   2885                             if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2886                             ptr = _m3d_getint(ptr, &n);
   2887                             model->voxel[i].uncertain = ((n > 0 && n < 256 ? n : 0) * 255) / 100;
   2888                             ptr = _m3d_findarg(ptr);
   2889                             if(*ptr && *ptr != '\r' && *ptr != '\n') {
   2890                                 ptr = _m3d_getint(ptr, &n);
   2891                                 model->voxel[i].groupid = n > 0 && n < 256 ? n : 0;
   2892                             }
   2893                         break;
   2894                         case 'p':
   2895                             ptr = _m3d_findarg(ptr);
   2896                             if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2897                             ptr = _m3d_getint(ptr, &n);
   2898                             model->voxel[i].x = n;
   2899                             ptr = _m3d_findarg(ptr);
   2900                             if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2901                             ptr = _m3d_getint(ptr, &n);
   2902                             model->voxel[i].y = n;
   2903                             ptr = _m3d_findarg(ptr);
   2904                             if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2905                             ptr = _m3d_getint(ptr, &n);
   2906                             model->voxel[i].z = n;
   2907                         break;
   2908                         case 'd':
   2909                             ptr = _m3d_findarg(ptr);
   2910                             if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2911                             ptr = _m3d_getint(ptr, &n);
   2912                             model->voxel[i].w = n;
   2913                             ptr = _m3d_findarg(ptr);
   2914                             if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2915                             ptr = _m3d_getint(ptr, &n);
   2916                             model->voxel[i].h = n;
   2917                             ptr = _m3d_findarg(ptr);
   2918                             if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   2919                             ptr = _m3d_getint(ptr, &n);
   2920                             model->voxel[i].d = n;
   2921                         break;
   2922                         case 'l':
   2923                             if(model->voxel[i].data) { l++; k = 0; }
   2924                             else {
   2925                                 if(!model->voxel[i].w || !model->voxel[i].h || !model->voxel[i].d) {
   2926                                     M3D_LOG("No voxel dimension before layer data");
   2927                                     goto asciiend;
   2928                                 }
   2929                                 model->voxel[i].data = (M3D_VOXEL*)M3D_MALLOC(
   2930                                     model->voxel[i].w * model->voxel[i].h * model->voxel[i].d * sizeof(M3D_VOXEL));
   2931                                 if(!model->voxel[i].data) goto memerr;
   2932                             }
   2933                         break;
   2934                         default:
   2935                             if(!model->voxel[i].data || l >= model->voxel[i].h || k >= model->voxel[i].d) {
   2936                                 M3D_LOG("Missing voxel attributes or out of bound data");
   2937                                 goto asciiend;
   2938                             }
   2939                             for(n = l * model->voxel[i].w * model->voxel[i].d + k * model->voxel[i].w;
   2940                                 j < model->voxel[i].w && *ptr && *ptr != '\r' && *ptr != '\n'; j++) {
   2941                                 ptr = _m3d_getint(ptr, &am);
   2942                                 if(am >= model->numvoxtype) goto asciiend;
   2943                                 model->voxel[i].data[n + j] = am;
   2944                             }
   2945                             k++;
   2946                         break;
   2947                     }
   2948                     ptr = _m3d_findnl(ptr);
   2949                 }
   2950             } else
   2951             /* mathematical shape */
   2952             if(!memcmp(pe, "Shape", 5)) {
   2953                 pe = _m3d_findarg(pe);
   2954                 if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
   2955                 pe = _m3d_safestr(pe, 0);
   2956                 if(!pe || !*pe) goto asciiend;
   2957                 i = model->numshape++;
   2958                 model->shape = (m3dh_t*)M3D_REALLOC(model->shape, model->numshape * sizeof(m3ds_t));
   2959                 if(!model->shape) goto memerr;
   2960                 h = &model->shape[i];
   2961                 h->name = pe;
   2962                 h->group = M3D_UNDEF;
   2963                 h->numcmd = 0;
   2964                 h->cmd = NULL;
   2965                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   2966                     if(!memcmp(ptr, "group", 5)) {
   2967                         ptr = _m3d_findarg(ptr);
   2968                         ptr = _m3d_getint(ptr, &h->group);
   2969                         ptr = _m3d_findnl(ptr);
   2970                         if(h->group != M3D_UNDEF && h->group >= model->numbone) {
   2971                             M3D_LOG("Unknown bone id as shape group in shape");
   2972                             M3D_LOG(pe);
   2973                             h->group = M3D_UNDEF;
   2974                             model->errcode = M3D_ERR_SHPE;
   2975                         }
   2976                         continue;
   2977                     }
   2978                     for(cd = NULL, k = 0; k < (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])); k++) {
   2979                         j = (unsigned int)strlen(m3d_commandtypes[k].key);
   2980                         if(!memcmp(ptr, m3d_commandtypes[k].key, j) && (ptr[j] == ' ' || ptr[j] == '\r' || ptr[j] == '\n'))
   2981                             { cd = &m3d_commandtypes[k]; break; }
   2982                     }
   2983                     if(cd) {
   2984                         j = h->numcmd++;
   2985                         h->cmd = (m3dc_t*)M3D_REALLOC(h->cmd, h->numcmd * sizeof(m3dc_t));
   2986                         if(!h->cmd) goto memerr;
   2987                         h->cmd[j].type = k;
   2988                         h->cmd[j].arg = (uint32_t*)M3D_MALLOC(cd->p * sizeof(uint32_t));
   2989                         if(!h->cmd[j].arg) goto memerr;
   2990                         memset(h->cmd[j].arg, 0, cd->p * sizeof(uint32_t));
   2991                         for(k = n = 0, l = cd->p; k < l; k++) {
   2992                             ptr = _m3d_findarg(ptr);
   2993                             if(!*ptr) goto asciiend;
   2994                             if(*ptr == '[') {
   2995                                 ptr = _m3d_findarg(ptr + 1);
   2996                                 if(!*ptr) goto asciiend;
   2997                             }
   2998                             if(*ptr == ']' || *ptr == '\r' || *ptr == '\n') break;
   2999                             switch(cd->a[((k - n) % (cd->p - n)) + n]) {
   3000                                 case m3dcp_mi_t:
   3001                                     mi = M3D_UNDEF;
   3002                                     if(*ptr != '\r' && *ptr != '\n') {
   3003                                         pe = _m3d_safestr(ptr, 0);
   3004                                         if(!pe || !*pe) goto asciiend;
   3005                                         for(n = 0; n < model->nummaterial; n++)
   3006                                             if(!strcmp(pe, model->material[n].name)) { mi = (M3D_INDEX)n; break; }
   3007                                         if(mi == M3D_UNDEF && !(model->flags & M3D_FLG_MTLLIB)) {
   3008                                             mi = model->nummaterial++;
   3009                                             model->material = (m3dm_t*)M3D_REALLOC(model->material,
   3010                                                 model->nummaterial * sizeof(m3dm_t));
   3011                                             if(!model->material) goto memerr;
   3012                                             model->material[mi].name = pe;
   3013                                             model->material[mi].numprop = 1;
   3014                                             model->material[mi].prop = NULL;
   3015                                         } else
   3016                                             M3D_FREE(pe);
   3017                                     }
   3018                                     h->cmd[j].arg[k] = mi;
   3019                                 break;
   3020                                 case m3dcp_vc_t:
   3021 #ifdef M3D_DOUBLE
   3022                                     _m3d_getfloat(ptr, &w); f = w;
   3023                                     memcpy(&h->cmd[j].arg[k], &f, 4);
   3024 #else
   3025                                     _m3d_getfloat(ptr, (float*)&h->cmd[j].arg[k]);
   3026 #endif
   3027                                 break;
   3028                                 case m3dcp_va_t:
   3029                                     ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]);
   3030                                     n = k + 1; l += (h->cmd[j].arg[k] - 1) * (cd->p - k - 1);
   3031                                     h->cmd[j].arg = (uint32_t*)M3D_REALLOC(h->cmd[j].arg, l * sizeof(uint32_t));
   3032                                     if(!h->cmd[j].arg) goto memerr;
   3033                                     memset(&h->cmd[j].arg[k + 1], 0, (l - k - 1) * sizeof(uint32_t));
   3034                                 break;
   3035                                 case m3dcp_qi_t:
   3036                                     ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]);
   3037                                     model->vertex[h->cmd[i].arg[k]].skinid = M3D_INDEXMAX;
   3038                                 break;
   3039                                 default:
   3040                                     ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]);
   3041                                 break;
   3042                             }
   3043                         }
   3044                     } else {
   3045                         M3D_LOG("Unknown shape command in");
   3046                         M3D_LOG(h->name);
   3047                         model->errcode = M3D_ERR_UNKCMD;
   3048                     }
   3049                     ptr = _m3d_findnl(ptr);
   3050                 }
   3051                 if(!h->numcmd) model->numshape--;
   3052             } else
   3053             /* annotation labels */
   3054             if(!memcmp(pe, "Labels", 6)) {
   3055                 pe = _m3d_findarg(pe);
   3056                 if(!*pe) goto asciiend;
   3057                 if(*pe == '\r' || *pe == '\n') pe = NULL;
   3058                 else pe = _m3d_safestr(pe, 0);
   3059                 k = 0; fn = NULL;
   3060                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   3061                     if(*ptr == 'c') {
   3062                         ptr = _m3d_findarg(ptr);
   3063                         if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
   3064                         ptr = _m3d_gethex(ptr, &k);
   3065                     } else
   3066                     if(*ptr == 'l') {
   3067                         ptr = _m3d_findarg(ptr);
   3068                         if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
   3069                         fn = _m3d_safestr(ptr, 2);
   3070                     } else {
   3071                         i = model->numlabel++;
   3072                         model->label = (m3dl_t*)M3D_REALLOC(model->label, model->numlabel * sizeof(m3dl_t));
   3073                         if(!model->label) goto memerr;
   3074                         model->label[i].name = pe;
   3075                         model->label[i].lang = fn;
   3076                         model->label[i].color = k;
   3077                         ptr = _m3d_getint(ptr, &j);
   3078                         model->label[i].vertexid = (M3D_INDEX)j;
   3079                         ptr = _m3d_findarg(ptr);
   3080                         if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
   3081                         model->label[i].text = _m3d_safestr(ptr, 2);
   3082                     }
   3083                     ptr = _m3d_findnl(ptr);
   3084                 }
   3085             } else
   3086             /* action */
   3087             if(!memcmp(pe, "Action", 6)) {
   3088                 pe = _m3d_findarg(pe);
   3089                 if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
   3090                 pe = _m3d_getint(pe, &k);
   3091                 pe = _m3d_findarg(pe);
   3092                 if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
   3093                 pe = _m3d_safestr(pe, 0);
   3094                 if(!pe || !*pe) goto asciiend;
   3095                 i = model->numaction++;
   3096                 model->action = (m3da_t*)M3D_REALLOC(model->action, model->numaction * sizeof(m3da_t));
   3097                 if(!model->action) goto memerr;
   3098                 a = &model->action[i];
   3099                 a->name = pe;
   3100                 a->durationmsec = k;
   3101                 /* skip the first frame marker as there's always at least one frame */
   3102                 a->numframe = 1;
   3103                 a->frame = (m3dfr_t*)M3D_MALLOC(sizeof(m3dfr_t));
   3104                 if(!a->frame) goto memerr;
   3105                 a->frame[0].msec = 0;
   3106                 a->frame[0].numtransform = 0;
   3107                 a->frame[0].transform = NULL;
   3108                 i = 0;
   3109                 if(*ptr == 'f')
   3110                     ptr = _m3d_findnl(ptr);
   3111                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   3112                     if(*ptr == 'f') {
   3113                         i = a->numframe++;
   3114                         a->frame = (m3dfr_t*)M3D_REALLOC(a->frame, a->numframe * sizeof(m3dfr_t));
   3115                         if(!a->frame) goto memerr;
   3116                         ptr = _m3d_findarg(ptr);
   3117                         ptr = _m3d_getint(ptr, &a->frame[i].msec);
   3118                         a->frame[i].numtransform = 0;
   3119                         a->frame[i].transform = NULL;
   3120                     } else {
   3121                         j = a->frame[i].numtransform++;
   3122                         a->frame[i].transform = (m3dtr_t*)M3D_REALLOC(a->frame[i].transform,
   3123                             a->frame[i].numtransform * sizeof(m3dtr_t));
   3124                         if(!a->frame[i].transform) goto memerr;
   3125                         ptr = _m3d_getint(ptr, &k);
   3126                         ptr = _m3d_findarg(ptr);
   3127                         if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   3128                         a->frame[i].transform[j].boneid = (M3D_INDEX)k;
   3129                         ptr = _m3d_getint(ptr, &k);
   3130                         ptr = _m3d_findarg(ptr);
   3131                         if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   3132                         a->frame[i].transform[j].pos = (M3D_INDEX)k;
   3133                         ptr = _m3d_getint(ptr, &k);
   3134                         if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
   3135                         a->frame[i].transform[j].ori = (M3D_INDEX)k;
   3136                         model->vertex[k].skinid = M3D_INDEXMAX;
   3137                     }
   3138                     ptr = _m3d_findnl(ptr);
   3139                 }
   3140             } else
   3141             /* inlined assets chunk */
   3142             if(!memcmp(pe, "Assets", 6)) {
   3143                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   3144                     if(readfilecb) {
   3145                         pe = _m3d_safestr(ptr, 2);
   3146                         if(!pe || !*pe) goto asciiend;
   3147                         i = model->numinlined++;
   3148                         model->inlined = (m3di_t*)M3D_REALLOC(model->inlined, model->numinlined * sizeof(m3di_t));
   3149                         if(!model->inlined) goto memerr;
   3150                         t = &model->inlined[i];
   3151                         model->inlined[i].data = (*readfilecb)(pe, &model->inlined[i].length);
   3152                         if(model->inlined[i].data) {
   3153                             fn = strrchr(pe, '.');
   3154                             if(fn && (fn[1] == 'p' || fn[1] == 'P') && (fn[2] == 'n' || fn[2] == 'N') &&
   3155                                 (fn[3] == 'g' || fn[3] == 'G')) *fn = 0;
   3156                             fn = strrchr(pe, '/');
   3157                             if(!fn) fn = strrchr(pe, '\\');
   3158                             if(!fn) fn = pe; else fn++;
   3159                             model->inlined[i].name = _m3d_safestr(fn, 0);
   3160                         } else
   3161                             model->numinlined--;
   3162                         M3D_FREE(pe);
   3163                     }
   3164                     ptr = _m3d_findnl(ptr);
   3165                 }
   3166             } else
   3167             /* extra chunks */
   3168             if(!memcmp(pe, "Extra", 5)) {
   3169                 pe = _m3d_findarg(pe);
   3170                 if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
   3171                 buff = (unsigned char*)_m3d_findnl(ptr);
   3172                 k = ((uint32_t)((uintptr_t)buff - (uintptr_t)ptr) / 3) + 1;
   3173                 i = model->numextra++;
   3174                 model->extra = (m3dchunk_t**)M3D_REALLOC(model->extra, model->numextra * sizeof(m3dchunk_t*));
   3175                 if(!model->extra) goto memerr;
   3176                 model->extra[i] = (m3dchunk_t*)M3D_MALLOC(k + sizeof(m3dchunk_t));
   3177                 if(!model->extra[i]) goto memerr;
   3178                 memcpy(&model->extra[i]->magic, pe, 4);
   3179                 model->extra[i]->length = sizeof(m3dchunk_t);
   3180                 pe = (char*)model->extra[i] + sizeof(m3dchunk_t);
   3181                 while(*ptr && *ptr != '\r' && *ptr != '\n') {
   3182                     ptr = _m3d_gethex(ptr, &k);
   3183                     *pe++ = (uint8_t)k;
   3184                     model->extra[i]->length++;
   3185                 }
   3186             } else
   3187                 goto asciiend;
   3188         }
   3189         model->errcode = M3D_SUCCESS;
   3190 asciiend:
   3191         setlocale(LC_NUMERIC, ol);
   3192         goto postprocess;
   3193     }
   3194 #endif
   3195     /* Binary variant */
   3196     len = ((m3dhdr_t*)data)->length - 8;
   3197     data += 8;
   3198     if(M3D_CHUNKMAGIC(data, 'P','R','V','W')) {
   3199         /* optional preview chunk */
   3200         model->preview.length = ((m3dchunk_t*)data)->length;
   3201         model->preview.data = data + sizeof(m3dchunk_t);
   3202         data += model->preview.length;
   3203         len -= model->preview.length;
   3204     }
   3205     if(!M3D_CHUNKMAGIC(data, 'H','E','A','D')) {
   3206         buff = (unsigned char *)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)data, len, 4096, (int*)&len, 1);
   3207         if(!buff || !len || !M3D_CHUNKMAGIC(buff, 'H','E','A','D')) {
   3208             if(buff) M3D_FREE(buff);
   3209             M3D_FREE(model);
   3210             return NULL;
   3211         }
   3212         buff = (unsigned char*)M3D_REALLOC(buff, len);
   3213         model->flags |= M3D_FLG_FREERAW; /* mark that we have to free the raw buffer */
   3214         data = buff;
   3215 #ifdef M3D_PROFILING
   3216         gettimeofday(&tv1, NULL);
   3217         tvd.tv_sec = tv1.tv_sec - tv0.tv_sec;
   3218         tvd.tv_usec = tv1.tv_usec - tv0.tv_usec;
   3219         if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
   3220         printf("  Deflate model   %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec);
   3221         memcpy(&tv0, &tv1, sizeof(struct timeval));
   3222 #endif
   3223     }
   3224     model->raw = (m3dhdr_t*)data;
   3225     end = data + len;
   3226 
   3227     /* parse header */
   3228     data += sizeof(m3dhdr_t);
   3229     M3D_LOG((char*)data);
   3230     model->name = (char*)data;
   3231     for(; data < end && *data; data++) {}; data++;
   3232     model->license = (char*)data;
   3233     for(; data < end && *data; data++) {}; data++;
   3234     model->author = (char*)data;
   3235     for(; data < end && *data; data++) {}; data++;
   3236     model->desc = (char*)data;
   3237     chunk = (unsigned char*)model->raw + model->raw->length;
   3238     model->scale = (M3D_FLOAT)model->raw->scale;
   3239     if(model->scale <= (M3D_FLOAT)0.0) model->scale = (M3D_FLOAT)1.0;
   3240     model->vc_s = 1 << ((model->raw->types >> 0) & 3);  /* vertex coordinate size */
   3241     model->vi_s = 1 << ((model->raw->types >> 2) & 3);  /* vertex index size */
   3242     model->si_s = 1 << ((model->raw->types >> 4) & 3);  /* string offset size */
   3243     model->ci_s = 1 << ((model->raw->types >> 6) & 3);  /* color index size */
   3244     model->ti_s = 1 << ((model->raw->types >> 8) & 3);  /* tmap index size */
   3245     model->bi_s = 1 << ((model->raw->types >>10) & 3);  /* bone index size */
   3246     model->nb_s = 1 << ((model->raw->types >>12) & 3);  /* number of bones per vertex */
   3247     model->sk_s = 1 << ((model->raw->types >>14) & 3);  /* skin index size */
   3248     model->fc_s = 1 << ((model->raw->types >>16) & 3);  /* frame counter size */
   3249     model->hi_s = 1 << ((model->raw->types >>18) & 3);  /* shape index size */
   3250     model->fi_s = 1 << ((model->raw->types >>20) & 3);  /* face index size */
   3251     model->vd_s = 1 << ((model->raw->types >>22) & 3);  /* voxel dimension size */
   3252     model->vp_s = 1 << ((model->raw->types >>24) & 3);  /* voxel pixel size */
   3253     if(model->ci_s == 8) model->ci_s = 0;               /* optional indices */
   3254     if(model->ti_s == 8) model->ti_s = 0;
   3255     if(model->bi_s == 8) model->bi_s = 0;
   3256     if(model->sk_s == 8) model->sk_s = 0;
   3257     if(model->fc_s == 8) model->fc_s = 0;
   3258     if(model->hi_s == 8) model->hi_s = 0;
   3259     if(model->fi_s == 8) model->fi_s = 0;
   3260 
   3261     /* variable limit checks */
   3262     if(sizeof(M3D_FLOAT) == 4 && model->vc_s > 4) {
   3263         M3D_LOG("Double precision coordinates not supported, truncating to float...");
   3264         model->errcode = M3D_ERR_TRUNC;
   3265     }
   3266     if((sizeof(M3D_INDEX) == 2 && (model->vi_s > 2 || model->si_s > 2 || model->ci_s > 2 || model->ti_s > 2 ||
   3267         model->bi_s > 2 || model->sk_s > 2 || model->fc_s > 2 || model->hi_s > 2 || model->fi_s > 2)) ||
   3268        (sizeof(M3D_VOXEL) < (size_t)model->vp_s && model->vp_s != 8)) {
   3269         M3D_LOG("32 bit indices not supported, unable to load model");
   3270         M3D_FREE(model);
   3271         return NULL;
   3272     }
   3273     if(model->vi_s > 4 || model->si_s > 4 || model->vp_s == 4) {
   3274         M3D_LOG("Invalid index size, unable to load model");
   3275         M3D_FREE(model);
   3276         return NULL;
   3277     }
   3278     if(!M3D_CHUNKMAGIC(end - 4, 'O','M','D','3')) {
   3279         M3D_LOG("Missing end chunk");
   3280         M3D_FREE(model);
   3281         return NULL;
   3282     }
   3283     if(model->nb_s > M3D_NUMBONE) {
   3284         M3D_LOG("Model has more bones per vertex than what importer was configured to support");
   3285         model->errcode = M3D_ERR_TRUNC;
   3286     }
   3287 
   3288     /* look for inlined assets in advance, material and procedural chunks may need them */
   3289     buff = chunk;
   3290     while(buff < end && !M3D_CHUNKMAGIC(buff, 'O','M','D','3')) {
   3291         data = buff;
   3292         len = ((m3dchunk_t*)data)->length;
   3293         buff += len;
   3294         if(len < sizeof(m3dchunk_t) || buff >= end) {
   3295             M3D_LOG("Invalid chunk size");
   3296             break;
   3297         }
   3298         len -= sizeof(m3dchunk_t) + model->si_s;
   3299 
   3300         /* inlined assets */
   3301         if(M3D_CHUNKMAGIC(data, 'A','S','E','T') && len > 0) {
   3302             M3D_LOG("Inlined asset");
   3303             i = model->numinlined++;
   3304             model->inlined = (m3di_t*)M3D_REALLOC(model->inlined, model->numinlined * sizeof(m3di_t));
   3305             if(!model->inlined) {
   3306 memerr:         M3D_LOG("Out of memory");
   3307                 model->errcode = M3D_ERR_ALLOC;
   3308                 return model;
   3309             }
   3310             data += sizeof(m3dchunk_t);
   3311             t = &model->inlined[i];
   3312             M3D_GETSTR(t->name);
   3313             M3D_LOG(t->name);
   3314             t->data = (uint8_t*)data;
   3315             t->length = len;
   3316         }
   3317     }
   3318 
   3319     /* parse chunks */
   3320     while(chunk < end && !M3D_CHUNKMAGIC(chunk, 'O','M','D','3')) {
   3321         data = chunk;
   3322         len = ((m3dchunk_t*)chunk)->length;
   3323         chunk += len;
   3324         if(len < sizeof(m3dchunk_t) || chunk >= end) {
   3325             M3D_LOG("Invalid chunk size");
   3326             break;
   3327         }
   3328         len -= sizeof(m3dchunk_t);
   3329 
   3330         /* color map */
   3331         if(M3D_CHUNKMAGIC(data, 'C','M','A','P')) {
   3332             M3D_LOG("Color map");
   3333             if(model->cmap) { M3D_LOG("More color map chunks, should be unique"); model->errcode = M3D_ERR_CMAP; continue; }
   3334             if(!model->ci_s) { M3D_LOG("Color map chunk, shouldn't be any"); model->errcode = M3D_ERR_CMAP; continue; }
   3335             model->numcmap = len / sizeof(uint32_t);
   3336             model->cmap = (uint32_t*)(data + sizeof(m3dchunk_t));
   3337         } else
   3338         /* texture map */
   3339         if(M3D_CHUNKMAGIC(data, 'T','M','A','P')) {
   3340             M3D_LOG("Texture map");
   3341             if(model->tmap) { M3D_LOG("More texture map chunks, should be unique"); model->errcode = M3D_ERR_TMAP; continue; }
   3342             if(!model->ti_s) { M3D_LOG("Texture map chunk, shouldn't be any"); model->errcode = M3D_ERR_TMAP; continue; }
   3343             reclen = model->vc_s + model->vc_s;
   3344             model->numtmap = len / reclen;
   3345             model->tmap = (m3dti_t*)M3D_MALLOC(model->numtmap * sizeof(m3dti_t));
   3346             if(!model->tmap) goto memerr;
   3347             for(i = 0, data += sizeof(m3dchunk_t); data < chunk; i++) {
   3348                 switch(model->vc_s) {
   3349                     case 1:
   3350                         model->tmap[i].u = (M3D_FLOAT)((uint8_t)data[0]) / (M3D_FLOAT)255.0;
   3351                         model->tmap[i].v = (M3D_FLOAT)((uint8_t)data[1]) / (M3D_FLOAT)255.0;
   3352                     break;
   3353                     case 2:
   3354                         model->tmap[i].u = (M3D_FLOAT)(*((uint16_t*)(data+0))) / (M3D_FLOAT)65535.0;
   3355                         model->tmap[i].v = (M3D_FLOAT)(*((uint16_t*)(data+2))) / (M3D_FLOAT)65535.0;
   3356                     break;
   3357                     case 4:
   3358                         model->tmap[i].u = (M3D_FLOAT)(*((float*)(data+0)));
   3359                         model->tmap[i].v = (M3D_FLOAT)(*((float*)(data+4)));
   3360                     break;
   3361                     case 8:
   3362                         model->tmap[i].u = (M3D_FLOAT)(*((double*)(data+0)));
   3363                         model->tmap[i].v = (M3D_FLOAT)(*((double*)(data+8)));
   3364                     break;
   3365                 }
   3366                 data += reclen;
   3367             }
   3368         } else
   3369         /* vertex list */
   3370         if(M3D_CHUNKMAGIC(data, 'V','R','T','S')) {
   3371             M3D_LOG("Vertex list");
   3372             if(model->vertex) { M3D_LOG("More vertex chunks, should be unique"); model->errcode = M3D_ERR_VRTS; continue; }
   3373             if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP;
   3374             reclen = model->ci_s + model->sk_s + 4 * model->vc_s;
   3375             model->numvertex = len / reclen;
   3376             model->vertex = (m3dv_t*)M3D_MALLOC(model->numvertex * sizeof(m3dv_t));
   3377             if(!model->vertex) goto memerr;
   3378             memset(model->vertex, 0, model->numvertex * sizeof(m3dv_t));
   3379             for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < model->numvertex; i++) {
   3380                 switch(model->vc_s) {
   3381                     case 1:
   3382                         model->vertex[i].x = (M3D_FLOAT)((int8_t)data[0]) / (M3D_FLOAT)127.0;
   3383                         model->vertex[i].y = (M3D_FLOAT)((int8_t)data[1]) / (M3D_FLOAT)127.0;
   3384                         model->vertex[i].z = (M3D_FLOAT)((int8_t)data[2]) / (M3D_FLOAT)127.0;
   3385                         model->vertex[i].w = (M3D_FLOAT)((int8_t)data[3]) / (M3D_FLOAT)127.0;
   3386                         data += 4;
   3387                     break;
   3388                     case 2:
   3389                         model->vertex[i].x = (M3D_FLOAT)(*((int16_t*)(data+0))) / (M3D_FLOAT)32767.0;
   3390                         model->vertex[i].y = (M3D_FLOAT)(*((int16_t*)(data+2))) / (M3D_FLOAT)32767.0;
   3391                         model->vertex[i].z = (M3D_FLOAT)(*((int16_t*)(data+4))) / (M3D_FLOAT)32767.0;
   3392                         model->vertex[i].w = (M3D_FLOAT)(*((int16_t*)(data+6))) / (M3D_FLOAT)32767.0;
   3393                         data += 8;
   3394                     break;
   3395                     case 4:
   3396                         model->vertex[i].x = (M3D_FLOAT)(*((float*)(data+0)));
   3397                         model->vertex[i].y = (M3D_FLOAT)(*((float*)(data+4)));
   3398                         model->vertex[i].z = (M3D_FLOAT)(*((float*)(data+8)));
   3399                         model->vertex[i].w = (M3D_FLOAT)(*((float*)(data+12)));
   3400                         data += 16;
   3401                     break;
   3402                     case 8:
   3403                         model->vertex[i].x = (M3D_FLOAT)(*((double*)(data+0)));
   3404                         model->vertex[i].y = (M3D_FLOAT)(*((double*)(data+8)));
   3405                         model->vertex[i].z = (M3D_FLOAT)(*((double*)(data+16)));
   3406                         model->vertex[i].w = (M3D_FLOAT)(*((double*)(data+24)));
   3407                         data += 32;
   3408                     break;
   3409                 }
   3410                 switch(model->ci_s) {
   3411                     case 1: model->vertex[i].color = model->cmap ? model->cmap[data[0]] : 0; data++; break;
   3412                     case 2: model->vertex[i].color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
   3413                     case 4: model->vertex[i].color = *((uint32_t*)data); data += 4; break;
   3414                     /* case 8: break; */
   3415                 }
   3416                 model->vertex[i].skinid = M3D_UNDEF;
   3417                 data = _m3d_getidx(data, model->sk_s, &model->vertex[i].skinid);
   3418             }
   3419         } else
   3420         /* skeleton: bone hierarchy and skin */
   3421         if(M3D_CHUNKMAGIC(data, 'B','O','N','E')) {
   3422             M3D_LOG("Skeleton");
   3423             if(model->bone) { M3D_LOG("More bone chunks, should be unique"); model->errcode = M3D_ERR_BONE; continue; }
   3424             if(!model->bi_s) { M3D_LOG("Bone chunk, shouldn't be any"); model->errcode=M3D_ERR_BONE; continue; }
   3425             if(!model->vertex) { M3D_LOG("No vertex chunk before bones"); model->errcode = M3D_ERR_VRTS; break; }
   3426             data += sizeof(m3dchunk_t);
   3427             model->numbone = 0;
   3428             data = _m3d_getidx(data, model->bi_s, &model->numbone);
   3429             if(model->numbone) {
   3430                 model->bone = (m3db_t*)M3D_MALLOC(model->numbone * sizeof(m3db_t));
   3431                 if(!model->bone) goto memerr;
   3432             }
   3433             model->numskin = 0;
   3434             data = _m3d_getidx(data, model->sk_s, &model->numskin);
   3435             /* read bone hierarchy */
   3436             for(i = 0; data < chunk && i < model->numbone; i++) {
   3437                 data = _m3d_getidx(data, model->bi_s, &model->bone[i].parent);
   3438                 M3D_GETSTR(model->bone[i].name);
   3439                 data = _m3d_getidx(data, model->vi_s, &model->bone[i].pos);
   3440                 data = _m3d_getidx(data, model->vi_s, &model->bone[i].ori);
   3441                 model->bone[i].numweight = 0;
   3442                 model->bone[i].weight = NULL;
   3443             }
   3444             if(i != model->numbone) { M3D_LOG("Truncated bone chunk"); model->numbone = i; model->numskin = 0; model->errcode = M3D_ERR_BONE; }
   3445             /* read skin definitions */
   3446             if(model->numskin) {
   3447                 model->skin = (m3ds_t*)M3D_MALLOC(model->numskin * sizeof(m3ds_t));
   3448                 if(!model->skin) goto memerr;
   3449                 for(i = 0; data < chunk && i < model->numskin; i++) {
   3450                     for(j = 0; j < M3D_NUMBONE; j++) {
   3451                         model->skin[i].boneid[j] = M3D_UNDEF;
   3452                         model->skin[i].weight[j] = (M3D_FLOAT)0.0;
   3453                     }
   3454                     memset(&weights, 0, sizeof(weights));
   3455                     if(model->nb_s == 1) weights[0] = 255;
   3456                     else {
   3457                         memcpy(&weights, data, model->nb_s);
   3458                         data += model->nb_s;
   3459                     }
   3460                     for(j = 0, w = (M3D_FLOAT)0.0; j < (unsigned int)model->nb_s; j++) {
   3461                         if(weights[j]) {
   3462                             if(j >= M3D_NUMBONE)
   3463                                 data += model->bi_s;
   3464                             else {
   3465                                 model->skin[i].weight[j] = (M3D_FLOAT)(weights[j]) / (M3D_FLOAT)255.0;
   3466                                 w += model->skin[i].weight[j];
   3467                                 data = _m3d_getidx(data, model->bi_s, &model->skin[i].boneid[j]);
   3468                             }
   3469                         }
   3470                     }
   3471                     /* this can occur if model has more bones than what the importer is configured to handle */
   3472                     if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0) {
   3473                         for(j = 0; j < M3D_NUMBONE; j++)
   3474                             model->skin[i].weight[j] /= w;
   3475                     }
   3476                 }
   3477                 if(i != model->numskin) { M3D_LOG("Truncated skin in bone chunk"); model->numskin = i; model->errcode = M3D_ERR_BONE; }
   3478             }
   3479         } else
   3480         /* material */
   3481         if(M3D_CHUNKMAGIC(data, 'M','T','R','L')) {
   3482             data += sizeof(m3dchunk_t);
   3483             M3D_GETSTR(name);
   3484             M3D_LOG("Material");
   3485             M3D_LOG(name);
   3486             if(model->ci_s < 4 && !model->numcmap) model->errcode = M3D_ERR_CMAP;
   3487             for(i = 0; i < model->nummaterial; i++)
   3488                 if(!strcmp(name, model->material[i].name)) {
   3489                     model->errcode = M3D_ERR_MTRL;
   3490                     M3D_LOG("Multiple definitions for material");
   3491                     M3D_LOG(name);
   3492                     name = NULL;
   3493                     break;
   3494                 }
   3495             if(name) {
   3496                 i = model->nummaterial++;
   3497                 if(model->flags & M3D_FLG_MTLLIB) {
   3498                     m = model->material;
   3499                     model->material = (m3dm_t*)M3D_MALLOC(model->nummaterial * sizeof(m3dm_t));
   3500                     if(!model->material) goto memerr;
   3501                     memcpy(model->material, m, (model->nummaterial - 1) * sizeof(m3dm_t));
   3502                     if(model->texture) {
   3503                         tx = model->texture;
   3504                         model->texture = (m3dtx_t*)M3D_MALLOC(model->numtexture * sizeof(m3dtx_t));
   3505                         if(!model->texture) goto memerr;
   3506                         memcpy(model->texture, tx, model->numtexture * sizeof(m3dm_t));
   3507                     }
   3508                     model->flags &= ~M3D_FLG_MTLLIB;
   3509                 } else {
   3510                     model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t));
   3511                     if(!model->material) goto memerr;
   3512                 }
   3513                 m = &model->material[i];
   3514                 m->numprop = 0;
   3515                 m->name = name;
   3516                 m->prop = (m3dp_t*)M3D_MALLOC((len / 2) * sizeof(m3dp_t));
   3517                 if(!m->prop) goto memerr;
   3518                 while(data < chunk) {
   3519                     i = m->numprop++;
   3520                     m->prop[i].type = *data++;
   3521                     m->prop[i].value.num = 0;
   3522                     if(m->prop[i].type >= 128)
   3523                         k = m3dpf_map;
   3524                     else {
   3525                         for(k = 256, j = 0; j < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); j++)
   3526                             if(m->prop[i].type == m3d_propertytypes[j].id) { k = m3d_propertytypes[j].format; break; }
   3527                     }
   3528                     switch(k) {
   3529                         case m3dpf_color:
   3530                             switch(model->ci_s) {
   3531                                 case 1: m->prop[i].value.color = model->cmap ? model->cmap[data[0]] : 0; data++; break;
   3532                                 case 2: m->prop[i].value.color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
   3533                                 case 4: m->prop[i].value.color = *((uint32_t*)data); data += 4; break;
   3534                             }
   3535                         break;
   3536 
   3537                         case m3dpf_uint8: m->prop[i].value.num = *data++; break;
   3538                         case m3dpf_uint16:m->prop[i].value.num = *((uint16_t*)data); data += 2; break;
   3539                         case m3dpf_uint32:m->prop[i].value.num = *((uint32_t*)data); data += 4; break;
   3540                         case m3dpf_float: m->prop[i].value.fnum = *((float*)data); data += 4; break;
   3541 
   3542                         case m3dpf_map:
   3543                             M3D_GETSTR(name);
   3544                             m->prop[i].value.textureid = _m3d_gettx(model, readfilecb, freecb, name);
   3545                             if(model->errcode == M3D_ERR_ALLOC) goto memerr;
   3546                             /* this error code only returned if readfilecb was specified */
   3547                             if(m->prop[i].value.textureid == M3D_UNDEF) {
   3548                                 M3D_LOG("Texture not found");
   3549                                 M3D_LOG(m->name);
   3550                                 m->numprop--;
   3551                             }
   3552                         break;
   3553 
   3554                         default:
   3555                             M3D_LOG("Unknown material property in");
   3556                             M3D_LOG(m->name);
   3557                             model->errcode = M3D_ERR_UNKPROP;
   3558                             data = chunk;
   3559                         break;
   3560                     }
   3561                 }
   3562                 m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
   3563                 if(!m->prop) goto memerr;
   3564             }
   3565         } else
   3566         /* face */
   3567         if(M3D_CHUNKMAGIC(data, 'P','R','O','C')) {
   3568             /* procedural surface */
   3569             M3D_GETSTR(name);
   3570             M3D_LOG("Procedural surface");
   3571             M3D_LOG(name);
   3572             _m3d_getpr(model, readfilecb, freecb, name);
   3573         } else
   3574         if(M3D_CHUNKMAGIC(data, 'M','E','S','H')) {
   3575             M3D_LOG("Mesh data");
   3576             if(!model->vertex) { M3D_LOG("No vertex chunk before mesh"); model->errcode = M3D_ERR_VRTS; }
   3577             /* mesh */
   3578             data += sizeof(m3dchunk_t);
   3579             mi = M3D_UNDEF;
   3580 #ifdef M3D_VERTEXMAX
   3581             pi = M3D_UNDEF;
   3582 #endif
   3583             am = model->numface;
   3584             while(data < chunk) {
   3585                 k = *data++;
   3586                 n = k >> 4;
   3587                 k &= 15;
   3588                 if(!n) {
   3589                     if(!k) {
   3590                         /* use material */
   3591                         mi = M3D_UNDEF;
   3592                         M3D_GETSTR(name);
   3593                         if(name) {
   3594                             for(j = 0; j < model->nummaterial; j++)
   3595                                 if(!strcmp(name, model->material[j].name)) {
   3596                                     mi = (M3D_INDEX)j;
   3597                                     break;
   3598                                 }
   3599                             if(mi == M3D_UNDEF) model->errcode = M3D_ERR_MTRL;
   3600                         }
   3601                     } else {
   3602                         /* use parameter */
   3603                         M3D_GETSTR(name);
   3604 #ifdef M3D_VERTEXMAX
   3605                         pi = M3D_UNDEF;
   3606                         if(name) {
   3607                             for(j = 0; j < model->numparam; j++)
   3608                                 if(!strcmp(name, model->param[j].name)) {
   3609                                     pi = (M3D_INDEX)j;
   3610                                     break;
   3611                                 }
   3612                             if(pi == M3D_UNDEF) {
   3613                                 pi = model->numparam++;
   3614                                 model->param = (m3dvi_t*)M3D_REALLOC(model->param, model->numparam * sizeof(m3dvi_t));
   3615                                 if(!model->param) goto memerr;
   3616                                 model->param[pi].name = name;
   3617                                 model->param[pi].count = 0;
   3618                             }
   3619                         }
   3620 #endif
   3621                     }
   3622                     continue;
   3623                 }
   3624                 if(n != 3) { M3D_LOG("Only triangle mesh supported for now"); model->errcode = M3D_ERR_UNKMESH; return model; }
   3625                 i = model->numface++;
   3626                 if(model->numface > am) {
   3627                     am = model->numface + 4095;
   3628                     model->face = (m3df_t*)M3D_REALLOC(model->face, am * sizeof(m3df_t));
   3629                     if(!model->face) goto memerr;
   3630                 }
   3631                 memset(&model->face[i], 255, sizeof(m3df_t)); /* set all index to -1 by default */
   3632                 model->face[i].materialid = mi;
   3633 #ifdef M3D_VERTEXMAX
   3634                 model->face[i].paramid = pi;
   3635 #endif
   3636                 for(j = 0; data < chunk && j < n; j++) {
   3637                     /* vertex */
   3638                     data = _m3d_getidx(data, model->vi_s, &model->face[i].vertex[j]);
   3639                     /* texcoord */
   3640                     if(k & 1)
   3641                         data = _m3d_getidx(data, model->ti_s, &model->face[i].texcoord[j]);
   3642                     /* normal */
   3643                     if(k & 2)
   3644                         data = _m3d_getidx(data, model->vi_s, &model->face[i].normal[j]);
   3645 #ifndef M3D_NONORMALS
   3646                     if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1;
   3647 #endif
   3648                     /* maximum */
   3649                     if(k & 4)
   3650 #ifdef M3D_VERTEXMAX
   3651                         data = _m3d_getidx(data, model->vi_s, &model->face[i].vertmax[j]);
   3652 #else
   3653                         data += model->vi_s;
   3654 #endif
   3655                 }
   3656                 if(j != n) { M3D_LOG("Invalid mesh"); model->numface = 0; model->errcode = M3D_ERR_UNKMESH; return model; }
   3657             }
   3658             model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t));
   3659         } else
   3660         if(M3D_CHUNKMAGIC(data, 'V','O','X','T')) {
   3661             /* voxel types */
   3662             M3D_LOG("Voxel types list");
   3663             if(model->voxtype) { M3D_LOG("More voxel type chunks, should be unique"); model->errcode = M3D_ERR_VOXT; continue; }
   3664             if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP;
   3665             reclen = model->ci_s + model->si_s + 3 + model->sk_s;
   3666             k = len / reclen;
   3667             model->voxtype = (m3dvt_t*)M3D_MALLOC(k * sizeof(m3dvt_t));
   3668             if(!model->voxtype) goto memerr;
   3669             memset(model->voxtype, 0, k * sizeof(m3dvt_t));
   3670             model->numvoxtype = 0;
   3671             for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < k; i++) {
   3672                 switch(model->ci_s) {
   3673                     case 1: model->voxtype[i].color = model->cmap ? model->cmap[data[0]] : 0; data++; break;
   3674                     case 2: model->voxtype[i].color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
   3675                     case 4: model->voxtype[i].color = *((uint32_t*)data); data += 4; break;
   3676                     /* case 8: break; */
   3677                 }
   3678                 M3D_GETSTR(name);
   3679                 model->voxtype[i].materialid = M3D_UNDEF;
   3680                 if(name) {
   3681                     model->voxtype[i].name = name;
   3682 /*
   3683                     for(j = 0; j < model->nummaterial; j++)
   3684                         if(!strcmp(name, model->material[j].name)) {
   3685                             model->voxtype[i].materialid = (M3D_INDEX)j;
   3686                             break;
   3687                         }
   3688 */
   3689                 }
   3690                 j = *data++;
   3691                 model->voxtype[i].rotation = j & 0xBF;
   3692                 model->voxtype[i].voxshape = ((j & 0x40) << 2) | *data++;
   3693                 model->voxtype[i].numitem = *data++;
   3694                 model->voxtype[i].skinid = M3D_UNDEF;
   3695                 data = _m3d_getidx(data, model->sk_s, &model->voxtype[i].skinid);
   3696                 if(model->voxtype[i].numitem) {
   3697                     model->voxtype[i].item = (m3dvi_t*)M3D_MALLOC(model->voxtype[i].numitem * sizeof(m3dvi_t));
   3698                     if(!model->voxtype[i].item) goto memerr;
   3699                     memset(model->voxtype[i].item, 0, model->voxtype[i].numitem * sizeof(m3dvi_t));
   3700                     for(j = 0; j < model->voxtype[i].numitem; j++) {
   3701                         model->voxtype[i].item[j].count = *data++;
   3702                         model->voxtype[i].item[j].count |= (*data++) << 8;
   3703                         M3D_GETSTR(model->voxtype[i].item[j].name);
   3704                     }
   3705                 }
   3706             }
   3707             model->numvoxtype = i;
   3708             if(k != model->numvoxtype) {
   3709                 model->voxtype = (m3dvt_t*)M3D_REALLOC(model->voxtype, model->numvoxtype * sizeof(m3dvt_t));
   3710                 if(!model->voxtype) goto memerr;
   3711             }
   3712         } else
   3713         if(M3D_CHUNKMAGIC(data, 'V','O','X','D')) {
   3714             /* voxel data */
   3715             data += sizeof(m3dchunk_t);
   3716             M3D_GETSTR(name);
   3717             M3D_LOG("Voxel Data Layer");
   3718             M3D_LOG(name);
   3719             if(model->vd_s > 4 || model->vp_s > 2) { M3D_LOG("No voxel index size"); model->errcode = M3D_ERR_UNKVOX; continue; }
   3720             if(!model->voxtype) { M3D_LOG("No voxel type chunk before voxel data"); model->errcode = M3D_ERR_VOXT; }
   3721             i = model->numvoxel++;
   3722             model->voxel = (m3dvx_t*)M3D_REALLOC(model->voxel, model->numvoxel * sizeof(m3dvx_t));
   3723             if(!model->voxel) goto memerr;
   3724             memset(&model->voxel[i], 0, sizeof(m3dvx_t));
   3725             model->voxel[i].name = name;
   3726             switch(model->vd_s) {
   3727                 case 1:
   3728                     model->voxel[i].x = (int32_t)((int8_t)data[0]);
   3729                     model->voxel[i].y = (int32_t)((int8_t)data[1]);
   3730                     model->voxel[i].z = (int32_t)((int8_t)data[2]);
   3731                     model->voxel[i].w = (uint32_t)(data[3]);
   3732                     model->voxel[i].h = (uint32_t)(data[4]);
   3733                     model->voxel[i].d = (uint32_t)(data[5]);
   3734                     data += 6;
   3735                 break;
   3736                 case 2:
   3737                     model->voxel[i].x = (int32_t)(*((int16_t*)(data+0)));
   3738                     model->voxel[i].y = (int32_t)(*((int16_t*)(data+2)));
   3739                     model->voxel[i].z = (int32_t)(*((int16_t*)(data+4)));
   3740                     model->voxel[i].w = (uint32_t)(*((uint16_t*)(data+6)));
   3741                     model->voxel[i].h = (uint32_t)(*((uint16_t*)(data+8)));
   3742                     model->voxel[i].d = (uint32_t)(*((uint16_t*)(data+10)));
   3743                     data += 12;
   3744                 break;
   3745                 case 4:
   3746                     model->voxel[i].x = *((int32_t*)(data+0));
   3747                     model->voxel[i].y = *((int32_t*)(data+4));
   3748                     model->voxel[i].z = *((int32_t*)(data+8));
   3749                     model->voxel[i].w = *((uint32_t*)(data+12));
   3750                     model->voxel[i].h = *((uint32_t*)(data+16));
   3751                     model->voxel[i].d = *((uint32_t*)(data+20));
   3752                     data += 24;
   3753                 break;
   3754             }
   3755             model->voxel[i].uncertain = *data++;
   3756             model->voxel[i].groupid = *data++;
   3757             k = model->voxel[i].w * model->voxel[i].h * model->voxel[i].d;
   3758             model->voxel[i].data = (M3D_VOXEL*)M3D_MALLOC(k * sizeof(M3D_VOXEL));
   3759             if(!model->voxel[i].data) goto memerr;
   3760             memset(model->voxel[i].data, 0xff, k * sizeof(M3D_VOXEL));
   3761             for(j = 0; data < chunk && j < k;) {
   3762                 l = ((*data++) & 0x7F) + 1;
   3763                 if(data[-1] & 0x80) {
   3764                     data = _m3d_getidx(data, model->vp_s, &mi);
   3765                     while(l-- && j < k) model->voxel[i].data[j++] = (M3D_VOXEL)mi;
   3766                 } else
   3767                     while(l-- && j < k) {
   3768                         data = _m3d_getidx(data, model->vp_s, &mi);
   3769                         model->voxel[i].data[j++] = (M3D_VOXEL)mi;
   3770                     }
   3771             }
   3772         } else
   3773         if(M3D_CHUNKMAGIC(data, 'S','H','P','E')) {
   3774             /* mathematical shape */
   3775             data += sizeof(m3dchunk_t);
   3776             M3D_GETSTR(name);
   3777             M3D_LOG("Mathematical Shape");
   3778             M3D_LOG(name);
   3779             i = model->numshape++;
   3780             model->shape = (m3dh_t*)M3D_REALLOC(model->shape, model->numshape * sizeof(m3dh_t));
   3781             if(!model->shape) goto memerr;
   3782             h = &model->shape[i];
   3783             h->numcmd = 0;
   3784             h->cmd = NULL;
   3785             h->name = name;
   3786             h->group = M3D_UNDEF;
   3787             data = _m3d_getidx(data, model->bi_s, &h->group);
   3788             if(h->group != M3D_UNDEF && h->group >= model->numbone) {
   3789                 M3D_LOG("Unknown bone id as shape group in shape");
   3790                 M3D_LOG(name);
   3791                 h->group = M3D_UNDEF;
   3792                 model->errcode = M3D_ERR_SHPE;
   3793             }
   3794             while(data < chunk) {
   3795                 i = h->numcmd++;
   3796                 h->cmd = (m3dc_t*)M3D_REALLOC(h->cmd, h->numcmd * sizeof(m3dc_t));
   3797                 if(!h->cmd) goto memerr;
   3798                 h->cmd[i].type = *data++;
   3799                 if(h->cmd[i].type & 0x80) {
   3800                     h->cmd[i].type &= 0x7F;
   3801                     h->cmd[i].type |= (*data++ << 7);
   3802                 }
   3803                 if(h->cmd[i].type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0]))) {
   3804                     M3D_LOG("Unknown shape command in");
   3805                     M3D_LOG(h->name);
   3806                     model->errcode = M3D_ERR_UNKCMD;
   3807                     break;
   3808                 }
   3809                 cd = &m3d_commandtypes[h->cmd[i].type];
   3810                 h->cmd[i].arg = (uint32_t*)M3D_MALLOC(cd->p * sizeof(uint32_t));
   3811                 if(!h->cmd[i].arg) goto memerr;
   3812                 memset(h->cmd[i].arg, 0, cd->p * sizeof(uint32_t));
   3813                 for(k = n = 0, l = cd->p; k < l; k++)
   3814                     switch(cd->a[((k - n) % (cd->p - n)) + n]) {
   3815                         case m3dcp_mi_t:
   3816                             h->cmd[i].arg[k] = M3D_NOTDEFINED;
   3817                             M3D_GETSTR(name);
   3818                             if(name) {
   3819                                 for(n = 0; n < model->nummaterial; n++)
   3820                                     if(!strcmp(name, model->material[n].name)) {
   3821                                         h->cmd[i].arg[k] = n;
   3822                                         break;
   3823                                     }
   3824                                 if(h->cmd[i].arg[k] == M3D_NOTDEFINED) model->errcode = M3D_ERR_MTRL;
   3825                             }
   3826                         break;
   3827                         case m3dcp_vc_t:
   3828                             f = 0.0f;
   3829                             switch(model->vc_s) {
   3830                                 case 1: f = (float)((int8_t)data[0]) / 127; break;
   3831                                 case 2: f = (float)(*((int16_t*)(data+0))) / 32767; break;
   3832                                 case 4: f = (float)(*((float*)(data+0))); break;
   3833                                 case 8: f = (float)(*((double*)(data+0))); break;
   3834                             }
   3835                             memcpy(&h->cmd[i].arg[k], &f, 4);
   3836                             data += model->vc_s;
   3837                         break;
   3838                         case m3dcp_hi_t: data = _m3d_getidx(data, model->hi_s, &h->cmd[i].arg[k]); break;
   3839                         case m3dcp_fi_t: data = _m3d_getidx(data, model->fi_s, &h->cmd[i].arg[k]); break;
   3840                         case m3dcp_ti_t: data = _m3d_getidx(data, model->ti_s, &h->cmd[i].arg[k]); break;
   3841                         case m3dcp_qi_t:
   3842                         case m3dcp_vi_t: data = _m3d_getidx(data, model->vi_s, &h->cmd[i].arg[k]); break;
   3843                         case m3dcp_i1_t: data = _m3d_getidx(data, 1, &h->cmd[i].arg[k]); break;
   3844                         case m3dcp_i2_t: data = _m3d_getidx(data, 2, &h->cmd[i].arg[k]); break;
   3845                         case m3dcp_i4_t: data = _m3d_getidx(data, 4, &h->cmd[i].arg[k]); break;
   3846                         case m3dcp_va_t: data = _m3d_getidx(data, 4, &h->cmd[i].arg[k]);
   3847                             n = k + 1; l += (h->cmd[i].arg[k] - 1) * (cd->p - k - 1);
   3848                             h->cmd[i].arg = (uint32_t*)M3D_REALLOC(h->cmd[i].arg, l * sizeof(uint32_t));
   3849                             if(!h->cmd[i].arg) goto memerr;
   3850                             memset(&h->cmd[i].arg[k + 1], 0, (l - k - 1) * sizeof(uint32_t));
   3851                         break;
   3852                     }
   3853             }
   3854         } else
   3855         /* annotation label list */
   3856         if(M3D_CHUNKMAGIC(data, 'L','B','L','S')) {
   3857             data += sizeof(m3dchunk_t);
   3858             M3D_GETSTR(name);
   3859             M3D_GETSTR(lang);
   3860             M3D_LOG("Label list");
   3861             if(name) { M3D_LOG(name); }
   3862             if(lang) { M3D_LOG(lang); }
   3863             if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP;
   3864             k = 0;
   3865             switch(model->ci_s) {
   3866                 case 1: k = model->cmap ? model->cmap[data[0]] : 0; data++; break;
   3867                 case 2: k = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
   3868                 case 4: k = *((uint32_t*)data); data += 4; break;
   3869                 /* case 8: break; */
   3870             }
   3871             reclen = model->vi_s + model->si_s;
   3872             i = model->numlabel; model->numlabel += len / reclen;
   3873             model->label = (m3dl_t*)M3D_REALLOC(model->label, model->numlabel * sizeof(m3dl_t));
   3874             if(!model->label) goto memerr;
   3875             memset(&model->label[i], 0, (model->numlabel - i) * sizeof(m3dl_t));
   3876             for(; data < chunk && i < model->numlabel; i++) {
   3877                 model->label[i].name = name;
   3878                 model->label[i].lang = lang;
   3879                 model->label[i].color = k;
   3880                 data = _m3d_getidx(data, model->vi_s, &model->label[i].vertexid);
   3881                 M3D_GETSTR(model->label[i].text);
   3882             }
   3883         } else
   3884         /* action */
   3885         if(M3D_CHUNKMAGIC(data, 'A','C','T','N')) {
   3886             M3D_LOG("Action");
   3887             i = model->numaction++;
   3888             model->action = (m3da_t*)M3D_REALLOC(model->action, model->numaction * sizeof(m3da_t));
   3889             if(!model->action) goto memerr;
   3890             a = &model->action[i];
   3891             data += sizeof(m3dchunk_t);
   3892             M3D_GETSTR(a->name);
   3893             M3D_LOG(a->name);
   3894             a->numframe = *((uint16_t*)data); data += 2;
   3895             if(a->numframe < 1) {
   3896                 model->numaction--;
   3897             } else {
   3898                 a->durationmsec = *((uint32_t*)data); data += 4;
   3899                 a->frame = (m3dfr_t*)M3D_MALLOC(a->numframe * sizeof(m3dfr_t));
   3900                 if(!a->frame) goto memerr;
   3901                 for(i = 0; data < chunk && i < a->numframe; i++) {
   3902                     a->frame[i].msec = *((uint32_t*)data); data += 4;
   3903                     a->frame[i].numtransform = 0; a->frame[i].transform = NULL;
   3904                     data = _m3d_getidx(data, model->fc_s, &a->frame[i].numtransform);
   3905                     if(a->frame[i].numtransform > 0) {
   3906                         a->frame[i].transform = (m3dtr_t*)M3D_MALLOC(a->frame[i].numtransform * sizeof(m3dtr_t));
   3907                         for(j = 0; j < a->frame[i].numtransform; j++) {
   3908                             data = _m3d_getidx(data, model->bi_s, &a->frame[i].transform[j].boneid);
   3909                             data = _m3d_getidx(data, model->vi_s, &a->frame[i].transform[j].pos);
   3910                             data = _m3d_getidx(data, model->vi_s, &a->frame[i].transform[j].ori);
   3911                         }
   3912                     }
   3913                 }
   3914             }
   3915         } else {
   3916             i = model->numextra++;
   3917             model->extra = (m3dchunk_t**)M3D_REALLOC(model->extra, model->numextra * sizeof(m3dchunk_t*));
   3918             if(!model->extra) goto memerr;
   3919             model->extra[i] = (m3dchunk_t*)data;
   3920         }
   3921     }
   3922     /* calculate normals, normalize skin weights, create bone/vertex cross-references and calculate transform matrices */
   3923 #ifdef M3D_ASCII
   3924 postprocess:
   3925 #endif
   3926     if(model) {
   3927         M3D_LOG("Post-process");
   3928 #ifdef M3D_PROFILING
   3929         gettimeofday(&tv1, NULL);
   3930         tvd.tv_sec = tv1.tv_sec - tv0.tv_sec;
   3931         tvd.tv_usec = tv1.tv_usec - tv0.tv_usec;
   3932         if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
   3933         printf("  Parsing chunks  %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec);
   3934 #endif
   3935 #ifndef M3D_NOVOXELS
   3936         if(model->numvoxel && model->voxel) {
   3937             M3D_LOG("Converting voxels into vertices and mesh");
   3938             /* add normals */
   3939             enorm = model->numvertex; model->numvertex += 6;
   3940             model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
   3941             if(!model->vertex) goto memerr;
   3942             memset(&model->vertex[enorm], 0, 6 * sizeof(m3dv_t));
   3943             for(l = 0; l < 6; l++)
   3944                 model->vertex[enorm+l].skinid = M3D_UNDEF;
   3945             model->vertex[enorm+0].y = (M3D_FLOAT)-1.0;
   3946             model->vertex[enorm+1].z = (M3D_FLOAT)-1.0;
   3947             model->vertex[enorm+2].x = (M3D_FLOAT)-1.0;
   3948             model->vertex[enorm+3].y = (M3D_FLOAT)1.0;
   3949             model->vertex[enorm+4].z = (M3D_FLOAT)1.0;
   3950             model->vertex[enorm+5].x = (M3D_FLOAT)1.0;
   3951             /* this is a fast, not so memory efficient version, only basic face culling used */
   3952             min_x = min_y = min_z = 2147483647L;
   3953             max_x = max_y = max_z = -2147483647L;
   3954             for(i = 0; i < model->numvoxel; i++) {
   3955                 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;
   3956                 if(model->voxel[i].x < min_x) min_x = model->voxel[i].x;
   3957                 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;
   3958                 if(model->voxel[i].y < min_y) min_y = model->voxel[i].y;
   3959                 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;
   3960                 if(model->voxel[i].z < min_z) min_z = model->voxel[i].z;
   3961             }
   3962             i = (-min_x > max_x ? -min_x : max_x);
   3963             j = (-min_y > max_y ? -min_y : max_y);
   3964             k = (-min_z > max_z ? -min_z : max_z);
   3965             if(j > i) i = j;
   3966             if(k > i) i = k;
   3967             if(i <= 1) i = 1;
   3968             w = (M3D_FLOAT)1.0 / (M3D_FLOAT)i;
   3969             if(i >= 254) model->vc_s = 2;
   3970             if(i >= 65534) model->vc_s = 4;
   3971             for(i = 0; i < model->numvoxel; i++) {
   3972                 sx = model->voxel[i].w; sz = model->voxel[i].d; sy = model->voxel[i].h;
   3973                 for(y = 0, j = 0; y < sy; y++)
   3974                     for(z = 0; z < sz; z++)
   3975                         for(x = 0; x < sx; x++, j++)
   3976                             if(model->voxel[i].data[j] < model->numvoxtype) {
   3977                                 k = 0;
   3978                                 /*  16__32     ____
   3979                                  *  /|  /|    /|2 /|
   3980                                  *64_128 |   /_8_/ 32
   3981                                  * | 1_|_2   |4|_|_|
   3982                                  * |/  |/    |/ 1|/
   3983                                  * 4___8     |16_|    */
   3984                                 k = n = am = 0;
   3985                                 if(!y || model->voxel[i].data[j - sx*sz] >= model->numvoxtype) { n++; am |= 1; k |= 1|2|4|8; }
   3986                                 if(!z || model->voxel[i].data[j - sx] >= model->numvoxtype) { n++; am |= 2; k |= 1|2|16|32; }
   3987                                 if(!x || model->voxel[i].data[j - 1] >= model->numvoxtype) { n++; am |= 4; k |= 1|4|16|64; }
   3988                                 if(y == sy-1 || model->voxel[i].data[j + sx*sz] >= model->numvoxtype) { n++; am |= 8; k |= 16|32|64|128; }
   3989                                 if(z == sz-1 || model->voxel[i].data[j + sx] >= model->numvoxtype) { n++; am |= 16; k |= 4|8|64|128; }
   3990                                 if(x == sx-1 || model->voxel[i].data[j + 1] >= model->numvoxtype) { n++; am |= 32; k |= 2|8|32|128; }
   3991                                 if(k) {
   3992                                     memset(edge, 255, sizeof(edge));
   3993                                     for(l = 0, len = 1, reclen = model->numvertex; l < 8; l++, len <<= 1)
   3994                                         if(k & len) edge[l] = model->numvertex++;
   3995                                     model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
   3996                                     if(!model->vertex) goto memerr;
   3997                                     memset(&model->vertex[reclen], 0, (model->numvertex-reclen) * sizeof(m3dv_t));
   3998                                     for(l = reclen; l < model->numvertex; l++) {
   3999                                         model->vertex[l].skinid = model->voxtype[model->voxel[i].data[j]].skinid;
   4000                                         model->vertex[l].color = model->voxtype[model->voxel[i].data[j]].color;
   4001                                     }
   4002                                     l = reclen;
   4003                                     if(k & 1) {
   4004                                         model->vertex[l].x = (model->voxel[i].x + x) * w;
   4005                                         model->vertex[l].y = (model->voxel[i].y + y) * w;
   4006                                         model->vertex[l].z = (model->voxel[i].z + z) * w;
   4007                                         l++;
   4008                                     }
   4009                                     if(k & 2) {
   4010                                         model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
   4011                                         model->vertex[l].y = (model->voxel[i].y + y) * w;
   4012                                         model->vertex[l].z = (model->voxel[i].z + z) * w;
   4013                                         l++;
   4014                                     }
   4015                                     if(k & 4) {
   4016                                         model->vertex[l].x = (model->voxel[i].x + x) * w;
   4017                                         model->vertex[l].y = (model->voxel[i].y + y) * w;
   4018                                         model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
   4019                                         l++;
   4020                                     }
   4021                                     if(k & 8) {
   4022                                         model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
   4023                                         model->vertex[l].y = (model->voxel[i].y + y) * w;
   4024                                         model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
   4025                                         l++;
   4026                                     }
   4027                                     if(k & 16) {
   4028                                         model->vertex[l].x = (model->voxel[i].x + x) * w;
   4029                                         model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
   4030                                         model->vertex[l].z = (model->voxel[i].z + z) * w;
   4031                                         l++;
   4032                                     }
   4033                                     if(k & 32) {
   4034                                         model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
   4035                                         model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
   4036                                         model->vertex[l].z = (model->voxel[i].z + z) * w;
   4037                                         l++;
   4038                                     }
   4039                                     if(k & 64) {
   4040                                         model->vertex[l].x = (model->voxel[i].x + x) * w;
   4041                                         model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
   4042                                         model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
   4043                                         l++;
   4044                                     }
   4045                                     if(k & 128) {
   4046                                         model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
   4047                                         model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
   4048                                         model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
   4049                                         l++;
   4050                                     }
   4051                                     n <<= 1;
   4052                                     l = model->numface; model->numface += n;
   4053                                     model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t));
   4054                                     if(!model->face) goto memerr;
   4055                                     memset(&model->face[l], 255, n * sizeof(m3df_t));
   4056                                     for(reclen = l; reclen < model->numface; reclen++)
   4057                                         model->face[reclen].materialid = model->voxtype[model->voxel[i].data[j]].materialid;
   4058                                     if(am & 1) {            /* bottom */
   4059                                         model->face[l].vertex[0] = edge[0];   model->face[l].vertex[1] = edge[1];   model->face[l].vertex[2] = edge[2];
   4060                                         model->face[l+1].vertex[0] = edge[2]; model->face[l+1].vertex[1] = edge[1]; model->face[l+1].vertex[2] = edge[3];
   4061                                         model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
   4062                                         model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm;
   4063                                         l += 2;
   4064                                     }
   4065                                     if(am & 2) {            /* north */
   4066                                         model->face[l].vertex[0] = edge[0];   model->face[l].vertex[1] = edge[4];   model->face[l].vertex[2] = edge[1];
   4067                                         model->face[l+1].vertex[0] = edge[1]; model->face[l+1].vertex[1] = edge[4]; model->face[l+1].vertex[2] = edge[5];
   4068                                         model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
   4069                                         model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+1;
   4070                                         l += 2;
   4071                                     }
   4072                                     if(am & 4) {            /* west */
   4073                                         model->face[l].vertex[0] = edge[0];   model->face[l].vertex[1] = edge[2];   model->face[l].vertex[2] = edge[4];
   4074                                         model->face[l+1].vertex[0] = edge[2]; model->face[l+1].vertex[1] = edge[6]; model->face[l+1].vertex[2] = edge[4];
   4075                                         model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
   4076                                         model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+2;
   4077                                         l += 2;
   4078                                     }
   4079                                     if(am & 8) {            /* top */
   4080                                         model->face[l].vertex[0] = edge[4];   model->face[l].vertex[1] = edge[6];   model->face[l].vertex[2] = edge[5];
   4081                                         model->face[l+1].vertex[0] = edge[5]; model->face[l+1].vertex[1] = edge[6]; model->face[l+1].vertex[2] = edge[7];
   4082                                         model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
   4083                                         model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+3;
   4084                                         l += 2;
   4085                                     }
   4086                                     if(am & 16) {           /* south */
   4087                                         model->face[l].vertex[0] = edge[2];   model->face[l].vertex[1] = edge[7];   model->face[l].vertex[2] = edge[6];
   4088                                         model->face[l+1].vertex[0] = edge[7]; model->face[l+1].vertex[1] = edge[2]; model->face[l+1].vertex[2] = edge[3];
   4089                                         model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
   4090                                         model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+4;
   4091                                         l += 2;
   4092                                     }
   4093                                     if(am & 32) {           /* east */
   4094                                         model->face[l].vertex[0] = edge[1];   model->face[l].vertex[1] = edge[5];   model->face[l].vertex[2] = edge[7];
   4095                                         model->face[l+1].vertex[0] = edge[1]; model->face[l+1].vertex[1] = edge[7]; model->face[l+1].vertex[2] = edge[3];
   4096                                         model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
   4097                                         model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+5;
   4098                                         l += 2;
   4099                                     }
   4100                                 }
   4101                             }
   4102             }
   4103         }
   4104 #endif
   4105 #ifndef M3D_NONORMALS
   4106         if(model->numface && model->face && neednorm) {
   4107             /* if they are missing, calculate triangle normals into a temporary buffer */
   4108             norm = (m3dv_t*)M3D_MALLOC(model->numface * sizeof(m3dv_t));
   4109             if(!norm) goto memerr;
   4110             for(i = 0, n = model->numvertex; i < model->numface; i++)
   4111                 if(model->face[i].normal[0] == M3D_UNDEF) {
   4112                     v0 = &model->vertex[model->face[i].vertex[0]];
   4113                     v1 = &model->vertex[model->face[i].vertex[1]];
   4114                     v2 = &model->vertex[model->face[i].vertex[2]];
   4115                     va.x = v1->x - v0->x; va.y = v1->y - v0->y; va.z = v1->z - v0->z;
   4116                     vb.x = v2->x - v0->x; vb.y = v2->y - v0->y; vb.z = v2->z - v0->z;
   4117                     v0 = &norm[i];
   4118                     v0->x = (va.y * vb.z) - (va.z * vb.y);
   4119                     v0->y = (va.z * vb.x) - (va.x * vb.z);
   4120                     v0->z = (va.x * vb.y) - (va.y * vb.x);
   4121                     w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z));
   4122                     v0->x *= w; v0->y *= w; v0->z *= w;
   4123                     model->face[i].normal[0] = model->face[i].vertex[0] + n;
   4124                     model->face[i].normal[1] = model->face[i].vertex[1] + n;
   4125                     model->face[i].normal[2] = model->face[i].vertex[2] + n;
   4126                 }
   4127             /* this is the fast way, we don't care if a normal is repeated in model->vertex */
   4128             M3D_LOG("Generating normals");
   4129             model->flags |= M3D_FLG_GENNORM;
   4130             model->numvertex <<= 1;
   4131             model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
   4132             if(!model->vertex) goto memerr;
   4133             memset(&model->vertex[n], 0, n * sizeof(m3dv_t));
   4134             for(i = 0; i < model->numface; i++)
   4135                 for(j = 0; j < 3; j++) {
   4136                     v0 = &model->vertex[model->face[i].vertex[j] + n];
   4137                     v0->x += norm[i].x;
   4138                     v0->y += norm[i].y;
   4139                     v0->z += norm[i].z;
   4140                 }
   4141             /* for each vertex, take the average of the temporary normals and use that */
   4142             for(i = 0, v0 = &model->vertex[n]; i < n; i++, v0++) {
   4143                 w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z));
   4144                 v0->x *= w; v0->y *= w; v0->z *= w;
   4145                 v0->skinid = M3D_UNDEF;
   4146             }
   4147             M3D_FREE(norm);
   4148         }
   4149 #endif
   4150         if(model->numbone && model->bone && model->numskin && model->skin && model->numvertex && model->vertex) {
   4151 #ifndef M3D_NOWEIGHTS
   4152             M3D_LOG("Generating weight cross-reference");
   4153             for(i = 0; i < model->numvertex; i++) {
   4154                 if(model->vertex[i].skinid < model->numskin) {
   4155                     sk = &model->skin[model->vertex[i].skinid];
   4156                     w = (M3D_FLOAT)0.0;
   4157                     for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++)
   4158                         w += sk->weight[j];
   4159                     for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++) {
   4160                         sk->weight[j] /= w;
   4161                         b = &model->bone[sk->boneid[j]];
   4162                         k = b->numweight++;
   4163                         b->weight = (m3dw_t*)M3D_REALLOC(b->weight, b->numweight * sizeof(m3da_t));
   4164                         if(!b->weight) goto memerr;
   4165                         b->weight[k].vertexid = i;
   4166                         b->weight[k].weight = sk->weight[j];
   4167                     }
   4168                 }
   4169             }
   4170 #endif
   4171 #ifndef M3D_NOANIMATION
   4172             M3D_LOG("Calculating bone transformation matrices");
   4173             for(i = 0; i < model->numbone; i++) {
   4174                 b = &model->bone[i];
   4175                 if(model->bone[i].parent == M3D_UNDEF) {
   4176                     _m3d_mat((M3D_FLOAT*)&b->mat4, &model->vertex[b->pos], &model->vertex[b->ori]);
   4177                 } else {
   4178                     _m3d_mat((M3D_FLOAT*)&r, &model->vertex[b->pos], &model->vertex[b->ori]);
   4179                     _m3d_mul((M3D_FLOAT*)&b->mat4, (M3D_FLOAT*)&model->bone[b->parent].mat4, (M3D_FLOAT*)&r);
   4180                 }
   4181             }
   4182             for(i = 0; i < model->numbone; i++)
   4183                 _m3d_inv((M3D_FLOAT*)&model->bone[i].mat4);
   4184 #endif
   4185         }
   4186 #ifdef M3D_PROFILING
   4187         gettimeofday(&tv0, NULL);
   4188         tvd.tv_sec = tv0.tv_sec - tv1.tv_sec;
   4189         tvd.tv_usec = tv0.tv_usec - tv1.tv_usec;
   4190         if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
   4191         printf("  Post-process    %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec);
   4192 #endif
   4193     }
   4194     return model;
   4195 }
   4196 
   4197 /**
   4198  * Calculates skeletons for animation frames, returns a working copy (should be freed after use)
   4199  */
   4200 m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t *skeleton)
   4201 {
   4202     unsigned int i;
   4203     M3D_INDEX s = frameid;
   4204     m3dfr_t *fr;
   4205 
   4206     if(!model || !model->numbone || !model->bone || (actionid != M3D_UNDEF && (!model->action ||
   4207         actionid >= model->numaction || frameid >= model->action[actionid].numframe))) {
   4208             model->errcode = M3D_ERR_UNKFRAME;
   4209             return skeleton;
   4210     }
   4211     model->errcode = M3D_SUCCESS;
   4212     if(!skeleton) {
   4213         skeleton = (m3dtr_t*)M3D_MALLOC(model->numbone * sizeof(m3dtr_t));
   4214         if(!skeleton) {
   4215             model->errcode = M3D_ERR_ALLOC;
   4216             return NULL;
   4217         }
   4218         goto gen;
   4219     }
   4220     if(actionid == M3D_UNDEF || !frameid) {
   4221 gen:    s = 0;
   4222         for(i = 0; i < model->numbone; i++) {
   4223             skeleton[i].boneid = i;
   4224             skeleton[i].pos = model->bone[i].pos;
   4225             skeleton[i].ori = model->bone[i].ori;
   4226         }
   4227     }
   4228     if(actionid < model->numaction && (frameid || !model->action[actionid].frame[0].msec)) {
   4229         for(; s <= frameid; s++) {
   4230             fr = &model->action[actionid].frame[s];
   4231             for(i = 0; i < fr->numtransform; i++) {
   4232                 skeleton[fr->transform[i].boneid].pos = fr->transform[i].pos;
   4233                 skeleton[fr->transform[i].boneid].ori = fr->transform[i].ori;
   4234             }
   4235         }
   4236     }
   4237     return skeleton;
   4238 }
   4239 
   4240 #ifndef M3D_NOANIMATION
   4241 /**
   4242  * Returns interpolated animation-pose, a working copy (should be freed after use)
   4243  */
   4244 m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec)
   4245 {
   4246     unsigned int i, j, l;
   4247     M3D_FLOAT r[16], t, c, d, s;
   4248     m3db_t *ret;
   4249     m3dv_t *v, *p, *f;
   4250     m3dtr_t *tmp;
   4251     m3dfr_t *fr;
   4252 
   4253     if(!model || !model->numbone || !model->bone) {
   4254         model->errcode = M3D_ERR_UNKFRAME;
   4255         return NULL;
   4256     }
   4257     ret = (m3db_t*)M3D_MALLOC(model->numbone * sizeof(m3db_t));
   4258     if(!ret) {
   4259         model->errcode = M3D_ERR_ALLOC;
   4260         return NULL;
   4261     }
   4262     memcpy(ret, model->bone, model->numbone * sizeof(m3db_t));
   4263     for(i = 0; i < model->numbone; i++)
   4264         _m3d_inv((M3D_FLOAT*)&ret[i].mat4);
   4265     if(!model->action || actionid >= model->numaction) {
   4266         model->errcode = M3D_ERR_UNKFRAME;
   4267         return ret;
   4268     }
   4269     msec %= model->action[actionid].durationmsec;
   4270     model->errcode = M3D_SUCCESS;
   4271     fr = &model->action[actionid].frame[0];
   4272     for(j = l = 0; j < model->action[actionid].numframe && model->action[actionid].frame[j].msec <= msec; j++) {
   4273         fr = &model->action[actionid].frame[j];
   4274         l = fr->msec;
   4275         for(i = 0; i < fr->numtransform; i++) {
   4276             ret[fr->transform[i].boneid].pos = fr->transform[i].pos;
   4277             ret[fr->transform[i].boneid].ori = fr->transform[i].ori;
   4278         }
   4279     }
   4280     if(l != msec) {
   4281         model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, (model->numvertex + 2 * model->numbone) * sizeof(m3dv_t));
   4282         if(!model->vertex) {
   4283             free(ret);
   4284             model->errcode = M3D_ERR_ALLOC;
   4285             return NULL;
   4286         }
   4287         tmp = (m3dtr_t*)M3D_MALLOC(model->numbone * sizeof(m3dtr_t));
   4288         if(tmp) {
   4289             for(i = 0; i < model->numbone; i++) {
   4290                 tmp[i].pos = ret[i].pos;
   4291                 tmp[i].ori = ret[i].ori;
   4292             }
   4293             fr = &model->action[actionid].frame[j % model->action[actionid].numframe];
   4294             t = l >= fr->msec ? (M3D_FLOAT)1.0 : (M3D_FLOAT)(msec - l) / (M3D_FLOAT)(fr->msec - l);
   4295             for(i = 0; i < fr->numtransform; i++) {
   4296                 tmp[fr->transform[i].boneid].pos = fr->transform[i].pos;
   4297                 tmp[fr->transform[i].boneid].ori = fr->transform[i].ori;
   4298             }
   4299             for(i = 0, j = model->numvertex; i < model->numbone; i++) {
   4300                 /* interpolation of position */
   4301                 if(ret[i].pos != tmp[i].pos) {
   4302                     p = &model->vertex[ret[i].pos];
   4303                     f = &model->vertex[tmp[i].pos];
   4304                     v = &model->vertex[j];
   4305                     v->x = p->x + t * (f->x - p->x);
   4306                     v->y = p->y + t * (f->y - p->y);
   4307                     v->z = p->z + t * (f->z - p->z);
   4308                     ret[i].pos = j++;
   4309                 }
   4310                 /* interpolation of orientation */
   4311                 if(ret[i].ori != tmp[i].ori) {
   4312                     p = &model->vertex[ret[i].ori];
   4313                     f = &model->vertex[tmp[i].ori];
   4314                     v = &model->vertex[j];
   4315                     d = p->w * f->w + p->x * f->x + p->y * f->y + p->z * f->z;
   4316                     if(d < 0) { d = -d; s = (M3D_FLOAT)-1.0; } else s = (M3D_FLOAT)1.0;
   4317 #if 0
   4318                     /* don't use SLERP, requires two more variables, libm linkage and it is slow (but nice) */
   4319                     a = (M3D_FLOAT)1.0 - t; b = t;
   4320                     if(d < (M3D_FLOAT)0.999999) { c = acosf(d); b = 1 / sinf(c); a = sinf(a * c) * b; b *= sinf(t * c) * s; }
   4321                     v->x = p->x * a + f->x * b;
   4322                     v->y = p->y * a + f->y * b;
   4323                     v->z = p->z * a + f->z * b;
   4324                     v->w = p->w * a + f->w * b;
   4325 #else
   4326                     /* approximated NLERP, original approximation by Arseny Kapoulkine, heavily optimized by me */
   4327                     c = t - (M3D_FLOAT)0.5; t += t * c * (t - (M3D_FLOAT)1.0) * (((M3D_FLOAT)1.0904 + d * ((M3D_FLOAT)-3.2452 +
   4328                         d * ((M3D_FLOAT)3.55645 - d * (M3D_FLOAT)1.43519))) * c * c + ((M3D_FLOAT)0.848013 + d *
   4329                         ((M3D_FLOAT)-1.06021 + d * (M3D_FLOAT)0.215638)));
   4330                     v->x = p->x + t * (s * f->x - p->x);
   4331                     v->y = p->y + t * (s * f->y - p->y);
   4332                     v->z = p->z + t * (s * f->z - p->z);
   4333                     v->w = p->w + t * (s * f->w - p->w);
   4334                     d = _m3d_rsq(v->w * v->w + v->x * v->x + v->y * v->y + v->z * v->z);
   4335                     v->x *= d; v->y *= d; v->z *= d; v->w *= d;
   4336 #endif
   4337                     ret[i].ori = j++;
   4338                 }
   4339             }
   4340             M3D_FREE(tmp);
   4341         }
   4342     }
   4343     for(i = 0; i < model->numbone; i++) {
   4344         if(ret[i].parent == M3D_UNDEF) {
   4345             _m3d_mat((M3D_FLOAT*)&ret[i].mat4, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]);
   4346         } else {
   4347             _m3d_mat((M3D_FLOAT*)&r, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]);
   4348             _m3d_mul((M3D_FLOAT*)&ret[i].mat4, (M3D_FLOAT*)&ret[ret[i].parent].mat4, (M3D_FLOAT*)&r);
   4349         }
   4350     }
   4351     return ret;
   4352 }
   4353 
   4354 #endif /* M3D_NOANIMATION */
   4355 
   4356 #endif /* M3D_IMPLEMENTATION */
   4357 
   4358 #if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_EXPORTER))
   4359 /**
   4360  * Free the in-memory model
   4361  */
   4362 void m3d_free(m3d_t *model)
   4363 {
   4364     unsigned int i, j;
   4365 
   4366     if(!model) return;
   4367 #ifdef M3D_ASCII
   4368     /* if model imported from ASCII, we have to free all strings as well */
   4369     if(model->flags & M3D_FLG_FREESTR) {
   4370         if(model->name) M3D_FREE(model->name);
   4371         if(model->license) M3D_FREE(model->license);
   4372         if(model->author) M3D_FREE(model->author);
   4373         if(model->desc) M3D_FREE(model->desc);
   4374         if(model->bone)
   4375             for(i = 0; i < model->numbone; i++)
   4376                 if(model->bone[i].name)
   4377                     M3D_FREE(model->bone[i].name);
   4378         if(model->shape)
   4379             for(i = 0; i < model->numshape; i++)
   4380                 if(model->shape[i].name)
   4381                     M3D_FREE(model->shape[i].name);
   4382         if(model->numvoxtype)
   4383             for(i = 0; i < model->numvoxtype; i++) {
   4384                 if(model->voxtype[i].name)
   4385                     M3D_FREE(model->voxtype[i].name);
   4386                 for(j = 0; j < model->voxtype[i].numitem; j++)
   4387                     if(model->voxtype[i].item[j].name)
   4388                         M3D_FREE(model->voxtype[i].item[j].name);
   4389             }
   4390         if(model->numvoxel)
   4391             for(i = 0; i < model->numvoxel; i++)
   4392                 if(model->voxel[i].name)
   4393                     M3D_FREE(model->voxel[i].name);
   4394         if(model->material)
   4395             for(i = 0; i < model->nummaterial; i++)
   4396                 if(model->material[i].name)
   4397                     M3D_FREE(model->material[i].name);
   4398         if(model->action)
   4399             for(i = 0; i < model->numaction; i++)
   4400                 if(model->action[i].name)
   4401                     M3D_FREE(model->action[i].name);
   4402         if(model->texture)
   4403             for(i = 0; i < model->numtexture; i++)
   4404                 if(model->texture[i].name)
   4405                     M3D_FREE(model->texture[i].name);
   4406         if(model->inlined)
   4407             for(i = 0; i < model->numinlined; i++) {
   4408                 if(model->inlined[i].name)
   4409                     M3D_FREE(model->inlined[i].name);
   4410                 if(model->inlined[i].data)
   4411                     M3D_FREE(model->inlined[i].data);
   4412             }
   4413         if(model->extra)
   4414             for(i = 0; i < model->numextra; i++)
   4415                 if(model->extra[i])
   4416                     M3D_FREE(model->extra[i]);
   4417         if(model->label)
   4418             for(i = 0; i < model->numlabel; i++) {
   4419                 if(model->label[i].name) {
   4420                     for(j = i + 1; j < model->numlabel; j++)
   4421                         if(model->label[j].name == model->label[i].name)
   4422                             model->label[j].name = NULL;
   4423                     M3D_FREE(model->label[i].name);
   4424                 }
   4425                 if(model->label[i].lang) {
   4426                     for(j = i + 1; j < model->numlabel; j++)
   4427                         if(model->label[j].lang == model->label[i].lang)
   4428                             model->label[j].lang = NULL;
   4429                     M3D_FREE(model->label[i].lang);
   4430                 }
   4431                 if(model->label[i].text)
   4432                     M3D_FREE(model->label[i].text);
   4433             }
   4434         if(model->preview.data)
   4435             M3D_FREE(model->preview.data);
   4436     }
   4437 #endif
   4438     if(model->flags & M3D_FLG_FREERAW) M3D_FREE(model->raw);
   4439 
   4440     if(model->tmap) M3D_FREE(model->tmap);
   4441     if(model->bone) {
   4442         for(i = 0; i < model->numbone; i++)
   4443             if(model->bone[i].weight)
   4444                 M3D_FREE(model->bone[i].weight);
   4445         M3D_FREE(model->bone);
   4446     }
   4447     if(model->skin) M3D_FREE(model->skin);
   4448     if(model->vertex) M3D_FREE(model->vertex);
   4449     if(model->face) M3D_FREE(model->face);
   4450     if(model->voxtype) {
   4451         for(i = 0; i < model->numvoxtype; i++)
   4452             if(model->voxtype[i].item)
   4453                 M3D_FREE(model->voxtype[i].item);
   4454         M3D_FREE(model->voxtype);
   4455     }
   4456     if(model->voxel) {
   4457         for(i = 0; i < model->numvoxel; i++)
   4458             if(model->voxel[i].data)
   4459                 M3D_FREE(model->voxel[i].data);
   4460         M3D_FREE(model->voxel);
   4461     }
   4462     if(model->shape) {
   4463         for(i = 0; i < model->numshape; i++) {
   4464             if(model->shape[i].cmd) {
   4465                 for(j = 0; j < model->shape[i].numcmd; j++)
   4466                     if(model->shape[i].cmd[j].arg) M3D_FREE(model->shape[i].cmd[j].arg);
   4467                 M3D_FREE(model->shape[i].cmd);
   4468             }
   4469         }
   4470         M3D_FREE(model->shape);
   4471     }
   4472     if(model->material && !(model->flags & M3D_FLG_MTLLIB)) {
   4473         for(i = 0; i < model->nummaterial; i++)
   4474             if(model->material[i].prop) M3D_FREE(model->material[i].prop);
   4475         M3D_FREE(model->material);
   4476     }
   4477     if(model->texture) {
   4478         for(i = 0; i < model->numtexture; i++)
   4479             if(model->texture[i].d) M3D_FREE(model->texture[i].d);
   4480         M3D_FREE(model->texture);
   4481     }
   4482     if(model->action) {
   4483         for(i = 0; i < model->numaction; i++) {
   4484             if(model->action[i].frame) {
   4485                 for(j = 0; j < model->action[i].numframe; j++)
   4486                     if(model->action[i].frame[j].transform) M3D_FREE(model->action[i].frame[j].transform);
   4487                 M3D_FREE(model->action[i].frame);
   4488             }
   4489         }
   4490         M3D_FREE(model->action);
   4491     }
   4492     if(model->label) M3D_FREE(model->label);
   4493     if(model->inlined) M3D_FREE(model->inlined);
   4494     if(model->extra) M3D_FREE(model->extra);
   4495     free(model);
   4496 }
   4497 #endif
   4498 
   4499 #ifdef M3D_EXPORTER
   4500 typedef struct {
   4501     char *str;
   4502     uint32_t offs;
   4503 } m3dstr_t;
   4504 
   4505 typedef struct {
   4506     m3dti_t data;
   4507     M3D_INDEX oldidx;
   4508     M3D_INDEX newidx;
   4509 } m3dtisave_t;
   4510 
   4511 typedef struct {
   4512     m3dv_t data;
   4513     M3D_INDEX oldidx;
   4514     M3D_INDEX newidx;
   4515     unsigned char norm;
   4516 } m3dvsave_t;
   4517 
   4518 typedef struct {
   4519     m3ds_t data;
   4520     M3D_INDEX oldidx;
   4521     M3D_INDEX newidx;
   4522 } m3dssave_t;
   4523 
   4524 typedef struct {
   4525     m3df_t data;
   4526     int group;
   4527     uint8_t opacity;
   4528 } m3dfsave_t;
   4529 
   4530 /* create unique list of strings */
   4531 static m3dstr_t *_m3d_addstr(m3dstr_t *str, uint32_t *numstr, char *s)
   4532 {
   4533     uint32_t i;
   4534     if(!s || !*s) return str;
   4535     if(str) {
   4536         for(i = 0; i < *numstr; i++)
   4537             if(str[i].str == s || !strcmp(str[i].str, s)) return str;
   4538     }
   4539     str = (m3dstr_t*)M3D_REALLOC(str, ((*numstr) + 1) * sizeof(m3dstr_t));
   4540     str[*numstr].str = s;
   4541     str[*numstr].offs = 0;
   4542     (*numstr)++;
   4543     return str;
   4544 }
   4545 
   4546 /* add strings to header */
   4547 m3dhdr_t *_m3d_addhdr(m3dhdr_t *h, m3dstr_t *s)
   4548 {
   4549     int i;
   4550     char *safe = _m3d_safestr(s->str, 0);
   4551     i = (int)strlen(safe);
   4552     h = (m3dhdr_t*)M3D_REALLOC(h, h->length + i+1);
   4553     if(!h) { M3D_FREE(safe); return NULL; }
   4554     memcpy((uint8_t*)h + h->length, safe, i+1);
   4555     s->offs = h->length - 16;
   4556     h->length += i+1;
   4557     M3D_FREE(safe);
   4558     return h;
   4559 }
   4560 
   4561 /* return offset of string */
   4562 static uint32_t _m3d_stridx(m3dstr_t *str, uint32_t numstr, char *s)
   4563 {
   4564     uint32_t i;
   4565     char *safe;
   4566     if(!s || !*s) return 0;
   4567     if(str) {
   4568         safe = _m3d_safestr(s, 0);
   4569         if(!safe) return 0;
   4570         if(!*safe) {
   4571             free(safe);
   4572             return 0;
   4573         }
   4574         for(i = 0; i < numstr; i++)
   4575             if(!strcmp(str[i].str, s)) {
   4576                 free(safe);
   4577                 return str[i].offs;
   4578             }
   4579         free(safe);
   4580     }
   4581     return 0;
   4582 }
   4583 
   4584 /* compare to faces by their material */
   4585 static int _m3d_facecmp(const void *a, const void *b) {
   4586     const m3dfsave_t *A = (const m3dfsave_t*)a, *B = (const m3dfsave_t*)b;
   4587     return A->group != B->group ? A->group - B->group : (A->opacity != B->opacity ? (int)B->opacity - (int)A->opacity :
   4588         (int)A->data.materialid - (int)B->data.materialid);
   4589 }
   4590 /* compare face groups */
   4591 static int _m3d_grpcmp(const void *a, const void *b) { return *((uint32_t*)a) - *((uint32_t*)b); }
   4592 /* compare UVs */
   4593 static int _m3d_ticmp(const void *a, const void *b) { return memcmp(a, b, sizeof(m3dti_t)); }
   4594 /* compare skin groups */
   4595 static int _m3d_skincmp(const void *a, const void *b) { return memcmp(a, b, sizeof(m3ds_t)); }
   4596 /* compare vertices */
   4597 static int _m3d_vrtxcmp(const void *a, const void *b) {
   4598     int c = memcmp(a, b, 3 * sizeof(M3D_FLOAT));
   4599     if(c) return c;
   4600     c = ((m3dvsave_t*)a)->norm - ((m3dvsave_t*)b)->norm;
   4601     if(c) return c;
   4602     return memcmp(a, b, sizeof(m3dv_t));
   4603 }
   4604 /* compare labels */
   4605 static _inline int _m3d_strcmp(char *a, char *b)
   4606 {
   4607     if(a == NULL && b != NULL) return -1;
   4608     if(a != NULL && b == NULL) return 1;
   4609     if(a == NULL && b == NULL) return 0;
   4610     return strcmp(a, b);
   4611 }
   4612 static int _m3d_lblcmp(const void *a, const void *b) {
   4613     const m3dl_t *A = (const m3dl_t*)a, *B = (const m3dl_t*)b;
   4614     int c = _m3d_strcmp(A->lang, B->lang);
   4615     if(!c) c = _m3d_strcmp(A->name, B->name);
   4616     if(!c) c = _m3d_strcmp(A->text, B->text);
   4617     return c;
   4618 }
   4619 /* compare two colors by HSV value */
   4620 _inline static int _m3d_cmapcmp(const void *a, const void *b)
   4621 {
   4622     uint8_t *A = (uint8_t*)a,  *B = (uint8_t*)b;
   4623     _register int m, vA, vB;
   4624     /* get HSV value for A */
   4625     m = A[2] < A[1]? A[2] : A[1]; if(A[0] < m) m = A[0];
   4626     vA = A[2] > A[1]? A[2] : A[1]; if(A[0] > vA) vA = A[0];
   4627     /* get HSV value for B */
   4628     m = B[2] < B[1]? B[2] : B[1]; if(B[0] < m) m = B[0];
   4629     vB = B[2] > B[1]? B[2] : B[1]; if(B[0] > vB) vB = B[0];
   4630     return vA - vB;
   4631 }
   4632 
   4633 /* create sorted list of colors */
   4634 static uint32_t *_m3d_addcmap(uint32_t *cmap, uint32_t *numcmap, uint32_t color)
   4635 {
   4636     uint32_t i;
   4637     if(cmap) {
   4638         for(i = 0; i < *numcmap; i++)
   4639             if(cmap[i] == color) return cmap;
   4640     }
   4641     cmap = (uint32_t*)M3D_REALLOC(cmap, ((*numcmap) + 1) * sizeof(uint32_t));
   4642     for(i = 0; i < *numcmap && _m3d_cmapcmp(&color, &cmap[i]) > 0; i++);
   4643     if(i < *numcmap) memmove(&cmap[i+1], &cmap[i], ((*numcmap) - i)*sizeof(uint32_t));
   4644     cmap[i] = color;
   4645     (*numcmap)++;
   4646     return cmap;
   4647 }
   4648 
   4649 /* look up a color and return its index */
   4650 static uint32_t _m3d_cmapidx(uint32_t *cmap, uint32_t numcmap, uint32_t color)
   4651 {
   4652     uint32_t i;
   4653     if(numcmap >= 65536)
   4654         return color;
   4655     for(i = 0; i < numcmap; i++)
   4656         if(cmap[i] == color) return i;
   4657     return 0;
   4658 }
   4659 
   4660 /* add index to output */
   4661 static unsigned char *_m3d_addidx(unsigned char *out, char type, uint32_t idx) {
   4662     switch(type) {
   4663         case 1: *out++ = (uint8_t)(idx); break;
   4664         case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break;
   4665         case 4: *((uint32_t*)out) = (uint32_t)(idx); out += 4; break;
   4666         /* case 0: case 8: break; */
   4667     }
   4668     return out;
   4669 }
   4670 
   4671 /* round a vertex position */
   4672 static void _m3d_round(int quality, m3dv_t *src, m3dv_t *dst)
   4673 {
   4674     _register int t;
   4675     /* copy additional attributes */
   4676     if(src != dst) memcpy(dst, src, sizeof(m3dv_t));
   4677     /* round according to quality */
   4678     switch(quality) {
   4679         case M3D_EXP_INT8:
   4680             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;
   4681             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;
   4682             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;
   4683             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;
   4684         break;
   4685         case M3D_EXP_INT16:
   4686             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;
   4687             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;
   4688             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;
   4689             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;
   4690         break;
   4691     }
   4692     if(dst->x == (M3D_FLOAT)-0.0) dst->x = (M3D_FLOAT)0.0;
   4693     if(dst->y == (M3D_FLOAT)-0.0) dst->y = (M3D_FLOAT)0.0;
   4694     if(dst->z == (M3D_FLOAT)-0.0) dst->z = (M3D_FLOAT)0.0;
   4695     if(dst->w == (M3D_FLOAT)-0.0) dst->w = (M3D_FLOAT)0.0;
   4696 }
   4697 
   4698 #ifdef M3D_ASCII
   4699 /* add a bone to ascii output */
   4700 static char *_m3d_prtbone(char *ptr, m3db_t *bone, M3D_INDEX numbone, M3D_INDEX parent, uint32_t level, M3D_INDEX *vrtxidx)
   4701 {
   4702     uint32_t i, j;
   4703     char *sn;
   4704 
   4705     if(level > M3D_BONEMAXLEVEL || !bone) return ptr;
   4706     for(i = 0; i < numbone; i++) {
   4707         if(bone[i].parent == parent) {
   4708             for(j = 0; j < level; j++) *ptr++ = '/';
   4709             sn = _m3d_safestr(bone[i].name, 0);
   4710             ptr += sprintf(ptr, "%d %d %s\r\n", vrtxidx[bone[i].pos], vrtxidx[bone[i].ori], sn);
   4711             M3D_FREE(sn);
   4712             ptr = _m3d_prtbone(ptr, bone, numbone, i, level + 1, vrtxidx);
   4713         }
   4714     }
   4715     return ptr;
   4716 }
   4717 #endif
   4718 
   4719 /**
   4720  * Function to encode an in-memory model into on storage Model 3D format
   4721  */
   4722 unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size)
   4723 {
   4724 #ifdef M3D_ASCII
   4725     const char *ol;
   4726     char *ptr;
   4727 #endif
   4728     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;
   4729     char *sn = NULL, *sl = NULL, *sa = NULL, *sd = NULL;
   4730     unsigned char *out = NULL, *z = NULL, weights[M3D_NUMBONE < 8 ? 8 : M3D_NUMBONE], *norm = NULL;
   4731     unsigned int i, j, k, l, n, o, len, chunklen, *length;
   4732     int maxvox = 0, minvox = 0;
   4733     M3D_FLOAT scale = (M3D_FLOAT)0.0, min_x, max_x, min_y, max_y, min_z, max_z, mw;
   4734     M3D_INDEX last, *vrtxidx = NULL, *mtrlidx = NULL, *tmapidx = NULL, *skinidx = NULL;
   4735 #ifdef M3D_VERTEXMAX
   4736     M3D_INDEX lastp;
   4737 #endif
   4738     uint32_t idx, numcmap = 0, *cmap = NULL, numvrtx = 0, maxvrtx = 0, numtmap = 0, maxtmap = 0, numproc = 0;
   4739     uint32_t numskin = 0, maxskin = 0, numstr = 0, maxt = 0, maxbone = 0, numgrp = 0, maxgrp = 0, *grpidx = NULL;
   4740     uint8_t *opa = NULL;
   4741     m3dcd_t *cd;
   4742     m3dc_t *cmd;
   4743     m3dstr_t *str = NULL;
   4744     m3dvsave_t *vrtx = NULL, vertex;
   4745     m3dtisave_t *tmap = NULL, tcoord;
   4746     m3dssave_t *skin = NULL, sk;
   4747     m3dfsave_t *face = NULL;
   4748     m3dhdr_t *h = NULL;
   4749     m3dm_t *m;
   4750     m3da_t *a;
   4751 
   4752     if(!model) {
   4753         if(size) *size = 0;
   4754         return NULL;
   4755     }
   4756     model->errcode = M3D_SUCCESS;
   4757 #ifdef M3D_ASCII
   4758     if(flags & M3D_EXP_ASCII) quality = M3D_EXP_DOUBLE;
   4759 #endif
   4760     vrtxidx = (M3D_INDEX*)M3D_MALLOC(model->numvertex * sizeof(M3D_INDEX));
   4761     if(!vrtxidx) goto memerr;
   4762     memset(vrtxidx, 255, model->numvertex * sizeof(M3D_INDEX));
   4763     if(model->numvertex && !(flags & M3D_EXP_NONORMAL)){
   4764         norm = (unsigned char*)M3D_MALLOC(model->numvertex * sizeof(unsigned char));
   4765         if(!norm) goto memerr;
   4766         memset(norm, 0, model->numvertex * sizeof(unsigned char));
   4767     }
   4768     if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) {
   4769         mtrlidx = (M3D_INDEX*)M3D_MALLOC(model->nummaterial * sizeof(M3D_INDEX));
   4770         if(!mtrlidx) goto memerr;
   4771         memset(mtrlidx, 255, model->nummaterial * sizeof(M3D_INDEX));
   4772         opa = (uint8_t*)M3D_MALLOC(model->nummaterial * 2 * sizeof(M3D_INDEX));
   4773         if(!opa) goto memerr;
   4774         memset(opa, 255, model->nummaterial * 2 * sizeof(M3D_INDEX));
   4775     }
   4776     if(model->numtmap && !(flags & M3D_EXP_NOTXTCRD)) {
   4777         tmapidx = (M3D_INDEX*)M3D_MALLOC(model->numtmap * sizeof(M3D_INDEX));
   4778         if(!tmapidx) goto memerr;
   4779         memset(tmapidx, 255, model->numtmap * sizeof(M3D_INDEX));
   4780     }
   4781     /** collect array elements that are actually referenced **/
   4782     if(!(flags & M3D_EXP_NOFACE)) {
   4783         /* face */
   4784         if(model->numface && model->face) {
   4785             M3D_LOG("Processing mesh face");
   4786             face = (m3dfsave_t*)M3D_MALLOC(model->numface * sizeof(m3dfsave_t));
   4787             if(!face) goto memerr;
   4788             for(i = 0; i < model->numface; i++) {
   4789                 memcpy(&face[i].data, &model->face[i], sizeof(m3df_t));
   4790                 face[i].group = 0;
   4791                 face[i].opacity = 255;
   4792                 if(!(flags & M3D_EXP_NOMATERIAL) && model->face[i].materialid < model->nummaterial) {
   4793                     if(model->material[model->face[i].materialid].numprop) {
   4794                         mtrlidx[model->face[i].materialid] = 0;
   4795                         if(opa[model->face[i].materialid * 2]) {
   4796                             m = &model->material[model->face[i].materialid];
   4797                             for(j = 0; j < m->numprop; j++)
   4798                                 if(m->prop[j].type == m3dp_Kd) {
   4799                                     opa[model->face[i].materialid * 2 + 1] = ((uint8_t*)&m->prop[j].value.color)[3];
   4800                                     break;
   4801                                 }
   4802                             for(j = 0; j < m->numprop; j++)
   4803                                 if(m->prop[j].type == m3dp_d) {
   4804                                     opa[model->face[i].materialid * 2 + 1] = (uint8_t)(m->prop[j].value.fnum * 255);
   4805                                     break;
   4806                                 }
   4807                             opa[model->face[i].materialid * 2] = 0;
   4808                         }
   4809                         face[i].opacity = opa[model->face[i].materialid * 2 + 1];
   4810                     } else
   4811                         face[i].data.materialid = M3D_UNDEF;
   4812                 }
   4813                 for(j = 0; j < 3; j++) {
   4814                     k = model->face[i].vertex[j];
   4815                     if(k < model->numvertex)
   4816                         vrtxidx[k] = 0;
   4817                     if(!(flags & M3D_EXP_NOCMAP)) {
   4818                         cmap = _m3d_addcmap(cmap, &numcmap, model->vertex[k].color);
   4819                         if(!cmap) goto memerr;
   4820                     }
   4821                     k = model->face[i].normal[j];
   4822                     if(k < model->numvertex && !(flags & M3D_EXP_NONORMAL)) {
   4823                         vrtxidx[k] = 0;
   4824                         norm[k] = 1;
   4825                     }
   4826                     k = model->face[i].texcoord[j];
   4827                     if(k < model->numtmap && !(flags & M3D_EXP_NOTXTCRD))
   4828                         tmapidx[k] = 0;
   4829 #ifdef M3D_VERTEXMAX
   4830                     k = model->face[i].vertmax[j];
   4831                     if(k < model->numvertex && !(flags & M3D_EXP_NOVRTMAX))
   4832                         vrtxidx[k] = 0;
   4833 #endif
   4834                 }
   4835                 /* convert from CW to CCW */
   4836                 if(flags & M3D_EXP_IDOSUCK) {
   4837                     j = face[i].data.vertex[1];
   4838                     face[i].data.vertex[1] = face[i].data.vertex[2];
   4839                     face[i].data.vertex[2] = j;
   4840                     j = face[i].data.normal[1];
   4841                     face[i].data.normal[1] = face[i].data.normal[2];
   4842                     face[i].data.normal[2] = j;
   4843                     j = face[i].data.texcoord[1];
   4844                     face[i].data.texcoord[1] = face[i].data.texcoord[2];
   4845                     face[i].data.texcoord[2] = j;
   4846 #ifdef M3D_VERTEXMAX
   4847                     j = face[i].data.vertmax[1];
   4848                     face[i].data.vertmax[1] = face[i].data.vertmax[2];
   4849                     face[i].data.vertmax[2] = j;
   4850 #endif
   4851                 }
   4852             }
   4853         }
   4854         if((model->numvoxtype && model->voxtype) || (model->numvoxel && model->voxel)) {
   4855             M3D_LOG("Processing voxel face");
   4856             for(i = 0; i < model->numvoxtype; i++) {
   4857                 str = _m3d_addstr(str, &numstr, model->voxtype[i].name);
   4858                 if(model->voxtype[i].name && !str) goto memerr;
   4859                 if(!(flags & M3D_EXP_NOCMAP)) {
   4860                     cmap = _m3d_addcmap(cmap, &numcmap, model->voxtype[i].color);
   4861                     if(!cmap) goto memerr;
   4862                 }
   4863                 for(j = 0; j < model->voxtype[i].numitem; j++) {
   4864                     str = _m3d_addstr(str, &numstr, model->voxtype[i].item[j].name);
   4865                     if(model->voxtype[i].item[j].name && !str) goto memerr;
   4866                 }
   4867             }
   4868             for(i = 0; i < model->numvoxel; i++) {
   4869                 str = _m3d_addstr(str, &numstr, model->voxel[i].name);
   4870                 if(model->voxel[i].name && !str) goto memerr;
   4871                 if(model->voxel[i].x < minvox) minvox = model->voxel[i].x;
   4872                 if(model->voxel[i].x + (int)model->voxel[i].w > maxvox) maxvox = model->voxel[i].x + model->voxel[i].w;
   4873                 if(model->voxel[i].y < minvox) minvox = model->voxel[i].y;
   4874                 if(model->voxel[i].y + (int)model->voxel[i].h > maxvox) maxvox = model->voxel[i].y + model->voxel[i].h;
   4875                 if(model->voxel[i].z < minvox) minvox = model->voxel[i].z;
   4876                 if(model->voxel[i].z + (int)model->voxel[i].d > maxvox) maxvox = model->voxel[i].z + model->voxel[i].d;
   4877             }
   4878         }
   4879         if(model->numshape && model->shape) {
   4880             M3D_LOG("Processing shape face");
   4881             for(i = 0; i < model->numshape; i++) {
   4882                 if(!model->shape[i].numcmd) continue;
   4883                 str = _m3d_addstr(str, &numstr, model->shape[i].name);
   4884                 if(model->shape[i].name && !str) goto memerr;
   4885                 for(j = 0; j < model->shape[i].numcmd; j++) {
   4886                     cmd = &model->shape[i].cmd[j];
   4887                     if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg)
   4888                         continue;
   4889                     if(cmd->type == m3dc_mesh) {
   4890                         if(numgrp + 2 < maxgrp) {
   4891                             maxgrp += 1024;
   4892                             grpidx = (uint32_t*)realloc(grpidx, maxgrp * sizeof(uint32_t));
   4893                             if(!grpidx) goto memerr;
   4894                             if(!numgrp) {
   4895                                 grpidx[0] = 0;
   4896                                 grpidx[1] = model->numface;
   4897                                 numgrp += 2;
   4898                             }
   4899                         }
   4900                         grpidx[numgrp + 0] = cmd->arg[0];
   4901                         grpidx[numgrp + 1] = cmd->arg[0] + cmd->arg[1];
   4902                         numgrp += 2;
   4903                     }
   4904                     cd = &m3d_commandtypes[cmd->type];
   4905                     for(k = n = 0, l = cd->p; k < l; k++)
   4906                         switch(cd->a[((k - n) % (cd->p - n)) + n]) {
   4907                             case m3dcp_mi_t:
   4908                                 if(!(flags & M3D_EXP_NOMATERIAL) && cmd->arg[k] < model->nummaterial)
   4909                                     mtrlidx[cmd->arg[k]] = 0;
   4910                             break;
   4911                             case m3dcp_ti_t:
   4912                                 if(!(flags & M3D_EXP_NOTXTCRD) && cmd->arg[k] < model->numtmap)
   4913                                     tmapidx[cmd->arg[k]] = 0;
   4914                             break;
   4915                             case m3dcp_qi_t:
   4916                             case m3dcp_vi_t:
   4917                                 if(cmd->arg[k] < model->numvertex)
   4918                                     vrtxidx[cmd->arg[k]] = 0;
   4919                             break;
   4920                             case m3dcp_va_t:
   4921                                 n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1);
   4922                             break;
   4923                         }
   4924                 }
   4925             }
   4926         }
   4927         if(model->numface && face) {
   4928             if(numgrp && grpidx) {
   4929                 qsort(grpidx, numgrp, sizeof(uint32_t), _m3d_grpcmp);
   4930                 for(i = j = 0; i < model->numface && j < numgrp; i++) {
   4931                     while(j < numgrp && grpidx[j] < i) j++;
   4932                     face[i].group = j;
   4933                 }
   4934             }
   4935             qsort(face, model->numface, sizeof(m3dfsave_t), _m3d_facecmp);
   4936         }
   4937         if(grpidx) { M3D_FREE(grpidx); grpidx = NULL; }
   4938         if(model->numlabel && model->label) {
   4939             M3D_LOG("Processing annotation labels");
   4940             for(i = 0; i < model->numlabel; i++) {
   4941                 str = _m3d_addstr(str, &numstr, model->label[i].name);
   4942                 str = _m3d_addstr(str, &numstr, model->label[i].lang);
   4943                 str = _m3d_addstr(str, &numstr, model->label[i].text);
   4944                 if(!(flags & M3D_EXP_NOCMAP)) {
   4945                     cmap = _m3d_addcmap(cmap, &numcmap, model->label[i].color);
   4946                     if(!cmap) goto memerr;
   4947                 }
   4948                 if(model->label[i].vertexid < model->numvertex)
   4949                     vrtxidx[model->label[i].vertexid] = 0;
   4950             }
   4951             qsort(model->label, model->numlabel, sizeof(m3dl_t), _m3d_lblcmp);
   4952         }
   4953     } else if(!(flags & M3D_EXP_NOMATERIAL)) {
   4954         /* without a face, simply add all materials, because it can be an mtllib */
   4955         for(i = 0; i < model->nummaterial; i++)
   4956             mtrlidx[i] = i;
   4957     }
   4958     /* bind-pose skeleton */
   4959     if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) {
   4960         M3D_LOG("Processing bones");
   4961         for(i = 0; i < model->numbone; i++) {
   4962             str = _m3d_addstr(str, &numstr, model->bone[i].name);
   4963             if(!str) goto memerr;
   4964             k = model->bone[i].pos;
   4965             if(k < model->numvertex)
   4966                 vrtxidx[k] = 0;
   4967             k = model->bone[i].ori;
   4968             if(k < model->numvertex)
   4969                 vrtxidx[k] = 0;
   4970         }
   4971     }
   4972     /* actions, animated skeleton poses */
   4973     if(model->numaction && model->action && !(flags & M3D_EXP_NOACTION)) {
   4974         M3D_LOG("Processing action list");
   4975         for(j = 0; j < model->numaction; j++) {
   4976             a = &model->action[j];
   4977             str = _m3d_addstr(str, &numstr, a->name);
   4978             if(!str) goto memerr;
   4979             if(a->numframe > 65535) a->numframe = 65535;
   4980             for(i = 0; i < a->numframe; i++) {
   4981                 for(l = 0; l < a->frame[i].numtransform; l++) {
   4982                     k = a->frame[i].transform[l].pos;
   4983                     if(k < model->numvertex)
   4984                         vrtxidx[k] = 0;
   4985                     k = a->frame[i].transform[l].ori;
   4986                     if(k < model->numvertex)
   4987                         vrtxidx[k] = 0;
   4988                 }
   4989                 if(l > maxt) maxt = l;
   4990             }
   4991         }
   4992     }
   4993     /* add colors to color map and texture names to string table */
   4994     if(!(flags & M3D_EXP_NOMATERIAL)) {
   4995         M3D_LOG("Processing materials");
   4996         for(i = k = 0; i < model->nummaterial; i++) {
   4997             if(mtrlidx[i] == M3D_UNDEF || !model->material[i].numprop) continue;
   4998             mtrlidx[i] = k++;
   4999             m = &model->material[i];
   5000             str = _m3d_addstr(str, &numstr, m->name);
   5001             if(!str) goto memerr;
   5002             if(m->prop)
   5003                 for(j = 0; j < m->numprop; j++) {
   5004                     if(!(flags & M3D_EXP_NOCMAP) && m->prop[j].type < 128) {
   5005                         for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) {
   5006                             if(m->prop[j].type == m3d_propertytypes[l].id && m3d_propertytypes[l].format == m3dpf_color) {
   5007                                 ((uint8_t*)&m->prop[j].value.color)[3] = opa[i * 2 + 1];
   5008                                 cmap = _m3d_addcmap(cmap, &numcmap, m->prop[j].value.color);
   5009                                 if(!cmap) goto memerr;
   5010                                 break;
   5011                             }
   5012                         }
   5013                     }
   5014                     if(m->prop[j].type >= 128 && m->prop[j].value.textureid < model->numtexture &&
   5015                         model->texture[m->prop[j].value.textureid].name) {
   5016                         str = _m3d_addstr(str, &numstr, model->texture[m->prop[j].value.textureid].name);
   5017                         if(!str) goto memerr;
   5018                     }
   5019                 }
   5020         }
   5021     }
   5022     /* if there's only one black color, don't store it */
   5023     if(numcmap == 1 && cmap && !cmap[0]) numcmap = 0;
   5024 
   5025     /** compress lists **/
   5026     if(model->numtmap && !(flags & M3D_EXP_NOTXTCRD)) {
   5027         M3D_LOG("Compressing tmap");
   5028         tmap = (m3dtisave_t*)M3D_MALLOC(model->numtmap * sizeof(m3dtisave_t));
   5029         if(!tmap) goto memerr;
   5030         for(i = 0; i < model->numtmap; i++) {
   5031             if(tmapidx[i] == M3D_UNDEF) continue;
   5032             switch(quality) {
   5033                 case M3D_EXP_INT8:
   5034                     l = (unsigned int)(model->tmap[i].u * 255); tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)255.0;
   5035                     l = (unsigned int)(model->tmap[i].v * 255); tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)255.0;
   5036                 break;
   5037                 case M3D_EXP_INT16:
   5038                     l = (unsigned int)(model->tmap[i].u * 65535); tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)65535.0;
   5039                     l = (unsigned int)(model->tmap[i].v * 65535); tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)65535.0;
   5040                 break;
   5041                 default:
   5042                     tcoord.data.u = model->tmap[i].u;
   5043                     tcoord.data.v = model->tmap[i].v;
   5044                 break;
   5045             }
   5046             if(flags & M3D_EXP_FLIPTXTCRD)
   5047                 tcoord.data.v = (M3D_FLOAT)1.0 - tcoord.data.v;
   5048             tcoord.oldidx = i;
   5049             memcpy(&tmap[numtmap++], &tcoord, sizeof(m3dtisave_t));
   5050         }
   5051         if(numtmap) {
   5052             qsort(tmap, numtmap, sizeof(m3dtisave_t), _m3d_ticmp);
   5053             memcpy(&tcoord.data, &tmap[0], sizeof(m3dti_t));
   5054             for(i = 0; i < numtmap; i++) {
   5055                 if(memcmp(&tcoord.data, &tmap[i].data, sizeof(m3dti_t))) {
   5056                     memcpy(&tcoord.data, &tmap[i].data, sizeof(m3dti_t));
   5057                     maxtmap++;
   5058                 }
   5059                 tmap[i].newidx = maxtmap;
   5060                 tmapidx[tmap[i].oldidx] = maxtmap;
   5061             }
   5062             maxtmap++;
   5063         }
   5064     }
   5065     if(model->numskin && model->skin && !(flags & M3D_EXP_NOBONE)) {
   5066         M3D_LOG("Compressing skin");
   5067         skinidx = (M3D_INDEX*)M3D_MALLOC(model->numskin * sizeof(M3D_INDEX));
   5068         if(!skinidx) goto memerr;
   5069         skin = (m3dssave_t*)M3D_MALLOC(model->numskin * sizeof(m3dssave_t));
   5070         if(!skin) goto memerr;
   5071         memset(skinidx, 255, model->numskin * sizeof(M3D_INDEX));
   5072         for(i = 0; i < model->numvertex; i++) {
   5073             if(vrtxidx[i] != M3D_UNDEF && model->vertex[i].skinid < model->numskin)
   5074                 skinidx[model->vertex[i].skinid] = 0;
   5075         }
   5076         for(i = 0; i < model->numskin; i++) {
   5077             if(skinidx[i] == M3D_UNDEF) continue;
   5078             memset(&sk, 0, sizeof(m3dssave_t));
   5079             for(j = 0, min_x = (M3D_FLOAT)0.0; j < M3D_NUMBONE && model->skin[i].boneid[j] != M3D_UNDEF; j++) {
   5080                     sk.data.boneid[j] = model->skin[i].boneid[j];
   5081                     sk.data.weight[j] = model->skin[i].weight[j] > (M3D_FLOAT)0.0 ? model->skin[i].weight[j] : (M3D_FLOAT)0.01;
   5082                     min_x += sk.data.weight[j];
   5083             }
   5084             if(j > maxbone) maxbone = j;
   5085             if(min_x != (M3D_FLOAT)1.0 && min_x != (M3D_FLOAT)0.0)
   5086                 for(j = 0; j < M3D_NUMBONE && sk.data.weight[j] > (M3D_FLOAT)0.0; j++)
   5087                     sk.data.weight[j] /= min_x;
   5088             sk.oldidx = i;
   5089             memcpy(&skin[numskin++], &sk, sizeof(m3dssave_t));
   5090         }
   5091         if(numskin) {
   5092             qsort(skin, numskin, sizeof(m3dssave_t), _m3d_skincmp);
   5093             memcpy(&sk.data, &skin[0].data, sizeof(m3ds_t));
   5094             for(i = 0; i < numskin; i++) {
   5095                 if(memcmp(&sk.data, &skin[i].data, sizeof(m3ds_t))) {
   5096                     memcpy(&sk.data, &skin[i].data, sizeof(m3ds_t));
   5097                     maxskin++;
   5098                 }
   5099                 skin[i].newidx = maxskin;
   5100                 skinidx[skin[i].oldidx] = maxskin;
   5101             }
   5102             maxskin++;
   5103         }
   5104     }
   5105 
   5106     M3D_LOG("Compressing vertex list");
   5107     min_x = min_y = min_z = (M3D_FLOAT)1e10;
   5108     max_x = max_y = max_z = (M3D_FLOAT)-1e10;
   5109     if(vrtxidx) {
   5110         vrtx = (m3dvsave_t*)M3D_MALLOC(model->numvertex * sizeof(m3dvsave_t));
   5111         if(!vrtx) goto memerr;
   5112         for(i = numvrtx = 0; i < model->numvertex; i++) {
   5113             if(vrtxidx[i] == M3D_UNDEF) continue;
   5114             _m3d_round(quality, &model->vertex[i], &vertex.data);
   5115             vertex.norm = norm ? norm[i] : 0;
   5116             if(vertex.data.skinid != M3D_INDEXMAX && !vertex.norm) {
   5117                 vertex.data.skinid = vertex.data.skinid != M3D_UNDEF && skinidx ? skinidx[vertex.data.skinid] : M3D_UNDEF;
   5118                 if(vertex.data.x > max_x) max_x = vertex.data.x;
   5119                 if(vertex.data.x < min_x) min_x = vertex.data.x;
   5120                 if(vertex.data.y > max_y) max_y = vertex.data.y;
   5121                 if(vertex.data.y < min_y) min_y = vertex.data.y;
   5122                 if(vertex.data.z > max_z) max_z = vertex.data.z;
   5123                 if(vertex.data.z < min_z) min_z = vertex.data.z;
   5124             }
   5125 #ifdef M3D_VERTEXTYPE
   5126             vertex.data.type = 0;
   5127 #endif
   5128             vertex.oldidx = i;
   5129             memcpy(&vrtx[numvrtx++], &vertex, sizeof(m3dvsave_t));
   5130         }
   5131         if(numvrtx) {
   5132             qsort(vrtx, numvrtx, sizeof(m3dvsave_t), _m3d_vrtxcmp);
   5133             memcpy(&vertex.data, &vrtx[0].data, sizeof(m3dv_t));
   5134             for(i = 0; i < numvrtx; i++) {
   5135                 if(memcmp(&vertex.data, &vrtx[i].data, vrtx[i].norm ? 3 * sizeof(M3D_FLOAT) : sizeof(m3dv_t))) {
   5136                     memcpy(&vertex.data, &vrtx[i].data, sizeof(m3dv_t));
   5137                     maxvrtx++;
   5138                 }
   5139                 vrtx[i].newidx = maxvrtx;
   5140                 vrtxidx[vrtx[i].oldidx] = maxvrtx;
   5141             }
   5142             maxvrtx++;
   5143         }
   5144     }
   5145     if(norm) { M3D_FREE(norm); norm = NULL; }
   5146 
   5147     /* normalize to bounding cube */
   5148     if(numvrtx && !(flags & M3D_EXP_NORECALC)) {
   5149         M3D_LOG("Normalizing coordinates");
   5150         if(min_x < (M3D_FLOAT)0.0) min_x = -min_x;
   5151         if(max_x < (M3D_FLOAT)0.0) max_x = -max_x;
   5152         if(min_y < (M3D_FLOAT)0.0) min_y = -min_y;
   5153         if(max_y < (M3D_FLOAT)0.0) max_y = -max_y;
   5154         if(min_z < (M3D_FLOAT)0.0) min_z = -min_z;
   5155         if(max_z < (M3D_FLOAT)0.0) max_z = -max_z;
   5156         scale = min_x;
   5157         if(max_x > scale) scale = max_x;
   5158         if(min_y > scale) scale = min_y;
   5159         if(max_y > scale) scale = max_y;
   5160         if(min_z > scale) scale = min_z;
   5161         if(max_z > scale) scale = max_z;
   5162         if(scale <= (M3D_FLOAT)0.0) scale = (M3D_FLOAT)1.0;
   5163         if(scale != (M3D_FLOAT)1.0) {
   5164             for(i = 0; i < numvrtx; i++) {
   5165                 if(vrtx[i].data.skinid == M3D_INDEXMAX) continue;
   5166                 vrtx[i].data.x /= scale;
   5167                 vrtx[i].data.y /= scale;
   5168                 vrtx[i].data.z /= scale;
   5169             }
   5170         }
   5171     }
   5172     if(model->scale > (M3D_FLOAT)0.0) scale = model->scale;
   5173     if(scale <= (M3D_FLOAT)0.0) scale = (M3D_FLOAT)1.0;
   5174 
   5175     /* meta info */
   5176     sn = _m3d_safestr(model->name && *model->name ? model->name : (char*)"(noname)", 2);
   5177     sl = _m3d_safestr(model->license ? model->license : (char*)"MIT", 2);
   5178     sa = _m3d_safestr(model->author ? model->author : getenv("LOGNAME"), 2);
   5179     if(!sn || !sl || !sa) {
   5180 memerr: if(vrtxidx) M3D_FREE(vrtxidx);
   5181         if(mtrlidx) M3D_FREE(mtrlidx);
   5182         if(tmapidx) M3D_FREE(tmapidx);
   5183         if(skinidx) M3D_FREE(skinidx);
   5184         if(grpidx) M3D_FREE(grpidx);
   5185         if(norm) M3D_FREE(norm);
   5186         if(face) M3D_FREE(face);
   5187         if(cmap) M3D_FREE(cmap);
   5188         if(tmap) M3D_FREE(tmap);
   5189         if(skin) M3D_FREE(skin);
   5190         if(str) M3D_FREE(str);
   5191         if(vrtx) M3D_FREE(vrtx);
   5192         if(sn) M3D_FREE(sn);
   5193         if(sl) M3D_FREE(sl);
   5194         if(sa) M3D_FREE(sa);
   5195         if(sd) M3D_FREE(sd);
   5196         if(out) M3D_FREE(out);
   5197         if(opa) free(opa);
   5198         if(h) M3D_FREE(h);
   5199         M3D_LOG("Out of memory");
   5200         model->errcode = M3D_ERR_ALLOC;
   5201         return NULL;
   5202     }
   5203 
   5204     M3D_LOG("Serializing model");
   5205 #ifdef M3D_ASCII
   5206     if(flags & M3D_EXP_ASCII) {
   5207         /* use CRLF to make model creators on Win happy... */
   5208         sd = _m3d_safestr(model->desc, 1);
   5209         if(!sd) goto memerr;
   5210         ol = setlocale(LC_NUMERIC, NULL);
   5211         setlocale(LC_NUMERIC, "C");
   5212         /* header */
   5213         len = 64 + (unsigned int)(strlen(sn) + strlen(sl) + strlen(sa) + strlen(sd));
   5214         out = (unsigned char*)M3D_MALLOC(len);
   5215         if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5216         ptr = (char*)out;
   5217         ptr += sprintf(ptr, "3dmodel %g\r\n%s\r\n%s\r\n%s\r\n%s\r\n\r\n", scale,
   5218             sn, sl, sa, sd);
   5219         M3D_FREE(sl); M3D_FREE(sa); M3D_FREE(sd);
   5220         sl = sa = sd = NULL;
   5221         /* preview chunk */
   5222         if(model->preview.data && model->preview.length) {
   5223             sl = _m3d_safestr(sn, 0);
   5224             if(sl) {
   5225 /* gcc thinks that "ptr is used after free", well, gcc is simply wrong. */
   5226 #ifdef __GNUC__
   5227 #pragma GCC diagnostic push
   5228 #pragma GCC diagnostic ignored "-Wuse-after-free"
   5229 #endif
   5230                 ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)20 + strlen(sl));
   5231                 out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5232 #ifdef __GNUC__
   5233 #pragma GCC diagnostic pop
   5234 #endif
   5235                 if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5236                 ptr += sprintf(ptr, "Preview\r\n%s.png\r\n\r\n", sl);
   5237                 M3D_FREE(sl); sl = NULL;
   5238             }
   5239         }
   5240         M3D_FREE(sn);  sn = NULL;
   5241         /* texture map */
   5242         if(numtmap && tmap && !(flags & M3D_EXP_NOTXTCRD) && !(flags & M3D_EXP_NOFACE)) {
   5243 /* interestingly gcc does not complain about "ptr is used after free" here, although the code is 100% the same */
   5244             ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(maxtmap * 32) + (uintptr_t)12);
   5245             out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5246             if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5247             ptr += sprintf(ptr, "Textmap\r\n");
   5248             last = M3D_UNDEF;
   5249             for(i = 0; i < numtmap; i++) {
   5250                 if(tmap[i].newidx == last) continue;
   5251                 last = tmap[i].newidx;
   5252                 ptr += sprintf(ptr, "%g %g\r\n", tmap[i].data.u, tmap[i].data.v);
   5253             }
   5254             ptr += sprintf(ptr, "\r\n");
   5255         }
   5256         /* vertex chunk */
   5257         if(numvrtx && vrtx && !(flags & M3D_EXP_NOFACE)) {
   5258             ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(maxvrtx * 128) + (uintptr_t)10);
   5259             out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5260             if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5261             ptr += sprintf(ptr, "Vertex\r\n");
   5262             last = M3D_UNDEF;
   5263             for(i = 0; i < numvrtx; i++) {
   5264                 if(vrtx[i].newidx == last) continue;
   5265                 last = vrtx[i].newidx;
   5266                 ptr += sprintf(ptr, "%g %g %g %g", vrtx[i].data.x, vrtx[i].data.y, vrtx[i].data.z, vrtx[i].data.w);
   5267                 if(!(flags & M3D_EXP_NOCMAP) && vrtx[i].data.color)
   5268                     ptr += sprintf(ptr, " #%08x", vrtx[i].data.color);
   5269                 if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin && vrtx[i].data.skinid < M3D_INDEXMAX) {
   5270                     if(skin[vrtx[i].data.skinid].data.weight[0] == (M3D_FLOAT)1.0)
   5271                         ptr += sprintf(ptr, " %d", skin[vrtx[i].data.skinid].data.boneid[0]);
   5272                     else
   5273                         for(j = 0; j < M3D_NUMBONE && skin[vrtx[i].data.skinid].data.boneid[j] != M3D_UNDEF &&
   5274                             skin[vrtx[i].data.skinid].data.weight[j] > (M3D_FLOAT)0.0; j++)
   5275                             ptr += sprintf(ptr, " %d:%g", skin[vrtx[i].data.skinid].data.boneid[j],
   5276                                 skin[vrtx[i].data.skinid].data.weight[j]);
   5277                 }
   5278                 ptr += sprintf(ptr, "\r\n");
   5279             }
   5280             ptr += sprintf(ptr, "\r\n");
   5281         }
   5282         /* bones chunk */
   5283         if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) {
   5284             ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)9);
   5285             for(i = 0; i < model->numbone; i++) {
   5286                 len += (unsigned int)strlen(model->bone[i].name) + 128;
   5287             }
   5288             out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5289             if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5290             ptr += sprintf(ptr, "Bones\r\n");
   5291             ptr = _m3d_prtbone(ptr, model->bone, model->numbone, M3D_UNDEF, 0, vrtxidx);
   5292             ptr += sprintf(ptr, "\r\n");
   5293         }
   5294         /* materials */
   5295         if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) {
   5296             for(j = 0; j < model->nummaterial; j++) {
   5297                 if(mtrlidx[j] == M3D_UNDEF || !model->material[j].numprop || !model->material[j].prop) continue;
   5298                 m = &model->material[j];
   5299                 sn = _m3d_safestr(m->name, 0);
   5300                 if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5301                 ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)12);
   5302                 for(i = 0; i < m->numprop; i++) {
   5303                     if(m->prop[i].type < 128)
   5304                         len += 32;
   5305                     else if(m->prop[i].value.textureid < model->numtexture && model->texture[m->prop[i].value.textureid].name)
   5306                         len += (unsigned int)strlen(model->texture[m->prop[i].value.textureid].name) + 16;
   5307                 }
   5308                 out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5309                 if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5310                 ptr += sprintf(ptr, "Material %s\r\n", sn);
   5311                 M3D_FREE(sn); sn = NULL;
   5312                 for(i = 0; i < m->numprop; i++) {
   5313                     k = 256;
   5314                     if(m->prop[i].type >= 128) {
   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                                 break;
   5319                             }
   5320                         if(!sn)
   5321                             for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++)
   5322                                 if(m->prop[i].type - 128 == m3d_propertytypes[l].id) {
   5323                                     sn = m3d_propertytypes[l].key;
   5324                                     break;
   5325                                 }
   5326                         k = sn ? m3dpf_map : 256;
   5327                     } else {
   5328                         for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++)
   5329                             if(m->prop[i].type == m3d_propertytypes[l].id) {
   5330                                 sn = m3d_propertytypes[l].key;
   5331                                 k = m3d_propertytypes[l].format;
   5332                                 break;
   5333                             }
   5334                     }
   5335                     switch(k) {
   5336                         case m3dpf_color: ptr += sprintf(ptr, "%s #%08x\r\n", sn, m->prop[i].value.color); break;
   5337                         case m3dpf_uint8:
   5338                         case m3dpf_uint16:
   5339                         case m3dpf_uint32: ptr += sprintf(ptr, "%s %d\r\n", sn, m->prop[i].value.num); break;
   5340                         case m3dpf_float:  ptr += sprintf(ptr, "%s %g\r\n", sn, m->prop[i].value.fnum); break;
   5341                         case m3dpf_map:
   5342                             if(m->prop[i].value.textureid < model->numtexture &&
   5343                                 model->texture[m->prop[i].value.textureid].name) {
   5344                                 sl = _m3d_safestr(model->texture[m->prop[i].value.textureid].name, 0);
   5345                                 if(!sl) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5346                                 if(*sl)
   5347                                     ptr += sprintf(ptr, "map_%s %s\r\n", sn, sl);
   5348                                 M3D_FREE(sn); M3D_FREE(sl); sl = NULL;
   5349                             }
   5350                         break;
   5351                     }
   5352                     sn = NULL;
   5353                 }
   5354                 ptr += sprintf(ptr, "\r\n");
   5355             }
   5356         }
   5357         /* procedural face */
   5358         if(model->numinlined && model->inlined && !(flags & M3D_EXP_NOFACE)) {
   5359             /* all inlined assets which are not textures should be procedural surfaces */
   5360             for(j = 0; j < model->numinlined; j++) {
   5361                 if(!model->inlined[j].name || !*model->inlined[j].name || !model->inlined[j].length || !model->inlined[j].data ||
   5362                  (model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' && model->inlined[j].data[3] == 'G'))
   5363                     continue;
   5364                 for(i = k = 0; i < model->numtexture; i++) {
   5365                     if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; }
   5366                 }
   5367                 if(k) continue;
   5368                 sn = _m3d_safestr(model->inlined[j].name, 0);
   5369                 if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5370                 ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)18);
   5371                 out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5372                 if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5373                 ptr += sprintf(ptr, "Procedural\r\n%s\r\n\r\n", sn);
   5374                 M3D_FREE(sn); sn = NULL;
   5375             }
   5376         }
   5377         /* mesh face */
   5378         if(model->numface && face && !(flags & M3D_EXP_NOFACE)) {
   5379             ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(model->numface * 128) + (uintptr_t)6);
   5380             last = M3D_UNDEF;
   5381 #ifdef M3D_VERTEXMAX
   5382             lastp = M3D_UNDEF;
   5383 #endif
   5384             if(!(flags & M3D_EXP_NOMATERIAL))
   5385                 for(i = 0; i < model->numface; i++) {
   5386                     j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : M3D_UNDEF;
   5387                     if(j != last) {
   5388                         last = j;
   5389                         if(last < model->nummaterial)
   5390                             len += (unsigned int)strlen(model->material[last].name);
   5391                         len += 6;
   5392                     }
   5393 #ifdef M3D_VERTEXMAX
   5394                     j = face[i].data.paramid < model->numparam ? face[i].data.paramid : M3D_UNDEF;
   5395                     if(j != lastp) {
   5396                         lastp = j;
   5397                         if(lastp < model->numparam)
   5398                             len += (unsigned int)strlen(model->param[lastp].name);
   5399                         len += 6;
   5400                     }
   5401 #endif
   5402                 }
   5403             out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5404             if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5405             ptr += sprintf(ptr, "Mesh\r\n");
   5406             last = M3D_UNDEF;
   5407 #ifdef M3D_VERTEXMAX
   5408             lastp = M3D_UNDEF;
   5409 #endif
   5410             for(i = 0; i < model->numface; i++) {
   5411                 j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : M3D_UNDEF;
   5412                 if(!(flags & M3D_EXP_NOMATERIAL) && j != last) {
   5413                     last = j;
   5414                     if(last < model->nummaterial) {
   5415                         sn = _m3d_safestr(model->material[last].name, 0);
   5416                         if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5417                         ptr += sprintf(ptr, "use %s\r\n", sn);
   5418                         M3D_FREE(sn); sn = NULL;
   5419                     } else
   5420                         ptr += sprintf(ptr, "use\r\n");
   5421                 }
   5422 #ifdef M3D_VERTEXMAX
   5423                 j = face[i].data.paramid < model->numparam ? face[i].data.paramid : M3D_UNDEF;
   5424                 if(!(flags & M3D_EXP_NOVRTMAX) && j != lastp) {
   5425                     lastp = j;
   5426                     if(lastp < model->numparam) {
   5427                         sn = _m3d_safestr(model->param[lastp].name, 0);
   5428                         if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5429                         ptr += sprintf(ptr, "par %s\r\n", sn);
   5430                         M3D_FREE(sn); sn = NULL;
   5431                     } else
   5432                         ptr += sprintf(ptr, "par\r\n");
   5433                 }
   5434 #endif
   5435                 /* hardcoded triangles. Should be repeated as many times as the number of edges in polygon */
   5436                 for(j = 0; j < 3; j++) {
   5437                     ptr += sprintf(ptr, "%s%d", j?" ":"", vrtxidx[face[i].data.vertex[j]]);
   5438                     k = l = M3D_NOTDEFINED;
   5439                     if(!(flags & M3D_EXP_NOTXTCRD) && (face[i].data.texcoord[j] != M3D_UNDEF) &&
   5440                         (tmapidx[face[i].data.texcoord[j]] != M3D_UNDEF)) {
   5441                             k = tmapidx[face[i].data.texcoord[j]];
   5442                             ptr += sprintf(ptr, "/%d", k);
   5443                     }
   5444                     if(!(flags & M3D_EXP_NONORMAL) && (face[i].data.normal[j] != M3D_UNDEF)) {
   5445                         l = vrtxidx[face[i].data.normal[j]];
   5446                         ptr += sprintf(ptr, "%s/%d", k == M3D_NOTDEFINED? "/" : "", l);
   5447                     }
   5448 #ifdef M3D_VERTEXMAX
   5449                     if(!(flags & M3D_EXP_NOVRTMAX) && (face[i].data.vertmax[j] != M3D_UNDEF)) {
   5450                         ptr += sprintf(ptr, "%s%s/%d", k == M3D_NOTDEFINED? "/" : "", l == M3D_NOTDEFINED? "/" : "",
   5451                             vrtxidx[face[i].data.vertmax[j]]);
   5452                     }
   5453 #endif
   5454                 }
   5455                 ptr += sprintf(ptr, "\r\n");
   5456             }
   5457             ptr += sprintf(ptr, "\r\n");
   5458         }
   5459         /* voxel face */
   5460         if(model->numvoxtype && model->voxtype && !(flags & M3D_EXP_NOFACE)) {
   5461             ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(model->numvoxtype * 128) + (uintptr_t)10);
   5462             for(i = 0; i < model->numvoxtype; i++) {
   5463                 if(model->voxtype[i].name) len += (unsigned int)strlen(model->voxtype[i].name);
   5464                 for(j = 0; j < model->voxtype[i].numitem; j++)
   5465                     if(model->voxtype[i].item[j].name)
   5466                         len += (unsigned int)strlen(model->voxtype[i].item[j].name) + 6;
   5467             }
   5468             out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5469             if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5470             ptr += sprintf(ptr, "VoxTypes\r\n");
   5471             for(i = 0; i < model->numvoxtype; i++) {
   5472                 ptr += sprintf(ptr, "#%08x", model->voxtype[i].color);
   5473                 if(model->voxtype[i].rotation)
   5474                     ptr += sprintf(ptr, "/%02x", model->voxtype[i].rotation);
   5475                 if(model->voxtype[i].voxshape)
   5476                     ptr += sprintf(ptr, "%s/%03x", model->voxtype[i].rotation ? "" : "/", model->voxtype[i].voxshape);
   5477                 sn = _m3d_safestr(model->voxtype[i].name, 0);
   5478                 if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5479                 ptr += sprintf(ptr, " %s", sn && sn[0] ? sn : "-");
   5480                 M3D_FREE(sn); sn = NULL;
   5481                 if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin && model->voxtype[i].skinid < M3D_INDEXMAX) {
   5482                     if(skin[skinidx[model->voxtype[i].skinid]].data.weight[0] == (M3D_FLOAT)1.0)
   5483                         ptr += sprintf(ptr, " %d", skin[skinidx[model->voxtype[i].skinid]].data.boneid[0]);
   5484                     else
   5485                         for(j = 0; j < M3D_NUMBONE && skin[skinidx[model->voxtype[i].skinid]].data.boneid[j] != M3D_UNDEF &&
   5486                             skin[skinidx[model->voxtype[i].skinid]].data.weight[j] > (M3D_FLOAT)0.0; j++)
   5487                             ptr += sprintf(ptr, " %d:%g", skin[skinidx[model->voxtype[i].skinid]].data.boneid[j],
   5488                                 skin[skinidx[model->voxtype[i].skinid]].data.weight[j]);
   5489                 }
   5490                 if(model->voxtype[i].numitem && model->voxtype[i].item) {
   5491                     for(j = k = 0; j < model->voxtype[i].numitem; j++) {
   5492                         if(!model->voxtype[i].item[j].count || !model->voxtype[i].item[j].name ||
   5493                             !model->voxtype[i].item[j].name[0]) continue;
   5494                         if(!k) { ptr += sprintf(ptr, " {"); k = 1; }
   5495                         sn = _m3d_safestr(model->voxtype[i].item[j].name, 0);
   5496                         if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5497                         ptr += sprintf(ptr, " %d %s", model->voxtype[i].item[j].count, sn);
   5498                         M3D_FREE(sn); sn = NULL;
   5499                     }
   5500                     if(k) ptr += sprintf(ptr, " }");
   5501                 }
   5502                 while(ptr[-1] == '-' || ptr[-1] == ' ') ptr--;
   5503                 ptr += sprintf(ptr, "\r\n");
   5504             }
   5505             ptr += sprintf(ptr, "\r\n");
   5506         }
   5507         if(model->numvoxel && model->voxel && !(flags & M3D_EXP_NOFACE)) {
   5508             for(i = 0; i < model->numvoxel; i++) {
   5509                 ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)128);
   5510                 if(model->voxel[i].name) len += (unsigned int)strlen(model->voxel[i].name);
   5511                 len += model->voxel[i].h * ((model->voxel[i].w * 6 + 2) * model->voxel[i].d + 9);
   5512                 out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5513                 if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5514                 ptr += sprintf(ptr, "Voxel");
   5515                 sn = _m3d_safestr(model->voxel[i].name, 0);
   5516                 if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5517                 if(sn && sn[0])
   5518                     ptr += sprintf(ptr, " %s", sn);
   5519                 M3D_FREE(sn); sn = NULL;
   5520                 ptr += sprintf(ptr, "\r\n");
   5521                 if(model->voxel[i].uncertain)
   5522                     ptr += sprintf(ptr, "uncertain %d %d\r\n", (model->voxel[i].uncertain * 100) / 255, model->voxel[i].groupid);
   5523                 if(model->voxel[i].x || model->voxel[i].y || model->voxel[i].z)
   5524                     ptr += sprintf(ptr, "pos %d %d %d\r\n", model->voxel[i].x, model->voxel[i].y, model->voxel[i].z);
   5525                 ptr += sprintf(ptr, "dim %d %d %d\r\n", model->voxel[i].w, model->voxel[i].h, model->voxel[i].d);
   5526                 for(j = n = 0; j < model->voxel[i].h; j++) {
   5527                     ptr += sprintf(ptr, "layer\r\n");
   5528                     for(k = 0; k < model->voxel[i].d; k++) {
   5529                         for(l = 0; l < model->voxel[i].w; l++, n++) {
   5530                             switch(model->voxel[i].data[n]) {
   5531                                 case M3D_VOXCLEAR: *ptr++ = '-'; break;
   5532                                 case M3D_VOXUNDEF: *ptr++ = '.'; break;
   5533                                 default: ptr += sprintf(ptr, "%d", model->voxel[i].data[n]); break;
   5534                             }
   5535                             *ptr++ = ' ';
   5536                         }
   5537                         ptr--;
   5538                         ptr += sprintf(ptr, "\r\n");
   5539                     }
   5540                 }
   5541                 ptr += sprintf(ptr, "\r\n");
   5542             }
   5543         }
   5544         /* mathematical shapes face */
   5545         if(model->numshape && model->numshape && !(flags & M3D_EXP_NOFACE)) {
   5546             for(j = 0; j < model->numshape; j++) {
   5547                 sn = _m3d_safestr(model->shape[j].name, 0);
   5548                 if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5549                 ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)33);
   5550                 out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5551                 if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5552                 ptr += sprintf(ptr, "Shape %s\r\n", sn);
   5553                 M3D_FREE(sn); sn = NULL;
   5554                 if(model->shape[j].group != M3D_UNDEF && !(flags & M3D_EXP_NOBONE))
   5555                     ptr += sprintf(ptr, "group %d\r\n", model->shape[j].group);
   5556                 for(i = 0; i < model->shape[j].numcmd; i++) {
   5557                     cmd = &model->shape[j].cmd[i];
   5558                     if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg)
   5559                         continue;
   5560                     cd = &m3d_commandtypes[cmd->type];
   5561                     ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(cd->key) + (uintptr_t)3);
   5562                     for(k = 0; k < cd->p; k++)
   5563                         switch(cd->a[k]) {
   5564                             case m3dcp_mi_t: if(cmd->arg[k] != M3D_NOTDEFINED) { len += (unsigned int)strlen(model->material[cmd->arg[k]].name) + 1; } break;
   5565                             case m3dcp_va_t: len += cmd->arg[k] * (cd->p - k - 1) * 16; k = cd->p; break;
   5566                             default: len += 16; break;
   5567                         }
   5568                     out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5569                     if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5570                     ptr += sprintf(ptr, "%s", cd->key);
   5571                     for(k = n = 0, l = cd->p; k < l; k++) {
   5572                         switch(cd->a[((k - n) % (cd->p - n)) + n]) {
   5573                             case m3dcp_mi_t:
   5574                                 if(cmd->arg[k] != M3D_NOTDEFINED) {
   5575                                     sn = _m3d_safestr(model->material[cmd->arg[k]].name, 0);
   5576                                     if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5577                                     ptr += sprintf(ptr, " %s", sn);
   5578                                     M3D_FREE(sn); sn = NULL;
   5579                                 }
   5580                             break;
   5581                             case m3dcp_vc_t: ptr += sprintf(ptr, " %g", *((float*)&cmd->arg[k])); break;
   5582                             case m3dcp_va_t: ptr += sprintf(ptr, " %d[", cmd->arg[k]);
   5583                                 n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1);
   5584                             break;
   5585                             default: ptr += sprintf(ptr, " %d", cmd->arg[k]); break;
   5586                         }
   5587                     }
   5588                     ptr += sprintf(ptr, "%s\r\n", l > cd->p ? " ]" : "");
   5589                 }
   5590                 ptr += sprintf(ptr, "\r\n");
   5591             }
   5592         }
   5593         /* annotation labels */
   5594         if(model->numlabel && model->label && !(flags & M3D_EXP_NOFACE)) {
   5595             for(i = 0, j = 3, length = NULL; i < model->numlabel; i++) {
   5596                 if(model->label[i].name) j += (unsigned int)strlen(model->label[i].name);
   5597                 if(model->label[i].lang) j += (unsigned int)strlen(model->label[i].lang);
   5598                 if(model->label[i].text) j += (unsigned int)strlen(model->label[i].text);
   5599                 j += 40;
   5600             }
   5601             ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)j);
   5602             out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5603             if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5604             for(i = 0; i < model->numlabel; i++) {
   5605                 if(!i || _m3d_strcmp(sl, model->label[i].lang) || _m3d_strcmp(sn, model->label[i].name)) {
   5606                     sl = model->label[i].lang;
   5607                     sn = model->label[i].name;
   5608                     sd = _m3d_safestr(sn, 0);
   5609                     if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; }
   5610                     if(i) ptr += sprintf(ptr, "\r\n");
   5611                     ptr += sprintf(ptr, "Labels %s\r\n", sd);
   5612                     M3D_FREE(sd); sd = NULL;
   5613                     if(model->label[i].color)
   5614                         ptr += sprintf(ptr, "color #0x%08x\r\n", model->label[i].color);
   5615                     if(sl && *sl) {
   5616                         sd = _m3d_safestr(sl, 0);
   5617                         if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; }
   5618                         ptr += sprintf(ptr, "lang %s\r\n", sd);
   5619                         M3D_FREE(sd); sd = NULL;
   5620                     }
   5621                 }
   5622                 sd = _m3d_safestr(model->label[i].text, 2);
   5623                 if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; }
   5624                 ptr += sprintf(ptr, "%d %s\r\n", model->label[i].vertexid, sd);
   5625                 M3D_FREE(sd); sd = NULL;
   5626             }
   5627             ptr += sprintf(ptr, "\r\n");
   5628             sn = sl = NULL;
   5629         }
   5630         /* actions */
   5631         if(model->numaction && model->action && !(flags & M3D_EXP_NOACTION)) {
   5632             for(j = 0; j < model->numaction; j++) {
   5633                 a = &model->action[j];
   5634                 sn = _m3d_safestr(a->name, 0);
   5635                 if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5636                 ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)48);
   5637                 for(i = 0; i < a->numframe; i++)
   5638                     len += a->frame[i].numtransform * 128 + 8;
   5639                 out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5640                 if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5641                 ptr += sprintf(ptr, "Action %d %s\r\n", a->durationmsec, sn);
   5642                 M3D_FREE(sn); sn = NULL;
   5643                 for(i = 0; i < a->numframe; i++) {
   5644                     ptr += sprintf(ptr, "frame %d\r\n", a->frame[i].msec);
   5645                     for(k = 0; k < a->frame[i].numtransform; k++) {
   5646                         ptr += sprintf(ptr, "%d %d %d\r\n", a->frame[i].transform[k].boneid,
   5647                             vrtxidx[a->frame[i].transform[k].pos], vrtxidx[a->frame[i].transform[k].ori]);
   5648                     }
   5649                 }
   5650                 ptr += sprintf(ptr, "\r\n");
   5651             }
   5652         }
   5653         /* inlined assets */
   5654         if(model->numinlined && model->inlined) {
   5655             for(i = j = 0; i < model->numinlined; i++)
   5656                 if(model->inlined[i].name)
   5657                     j += (unsigned int)strlen(model->inlined[i].name) + 6;
   5658             if(j > 0) {
   5659                 ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)j + (uintptr_t)16);
   5660                 out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5661                 if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5662                 ptr += sprintf(ptr, "Assets\r\n");
   5663                 for(i = 0; i < model->numinlined; i++)
   5664                     if(model->inlined[i].name)
   5665                         ptr += sprintf(ptr, "%s%s\r\n", model->inlined[i].name, strrchr(model->inlined[i].name, '.') ? "" : ".png");
   5666                 ptr += sprintf(ptr, "\r\n");
   5667             }
   5668         }
   5669         /* extra info */
   5670         if(model->numextra && (flags & M3D_EXP_EXTRA)) {
   5671             for(i = 0; i < model->numextra; i++) {
   5672                 if(model->extra[i]->length < 9) continue;
   5673                 ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)17 + (uintptr_t)(model->extra[i]->length * 3));
   5674                 out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
   5675                 if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
   5676                 ptr += sprintf(ptr, "Extra %c%c%c%c\r\n",
   5677                     model->extra[i]->magic[0] > ' ' ? model->extra[i]->magic[0] : '_',
   5678                     model->extra[i]->magic[1] > ' ' ? model->extra[i]->magic[1] : '_',
   5679                     model->extra[i]->magic[2] > ' ' ? model->extra[i]->magic[2] : '_',
   5680                     model->extra[i]->magic[3] > ' ' ? model->extra[i]->magic[3] : '_');
   5681                 for(j = 0; j < model->extra[i]->length; j++)
   5682                     ptr += sprintf(ptr, "%02x ", *((unsigned char *)model->extra + sizeof(m3dchunk_t) + j));
   5683                 ptr--;
   5684                 ptr += sprintf(ptr, "\r\n\r\n");
   5685             }
   5686         }
   5687         setlocale(LC_NUMERIC, ol);
   5688         len = (unsigned int)((uintptr_t)ptr - (uintptr_t)out);
   5689         out = (unsigned char*)M3D_REALLOC(out, len + 1);
   5690         if(!out) goto memerr;
   5691         out[len] = 0;
   5692     } else
   5693 #endif
   5694     {
   5695         /* stricly only use LF (newline) in binary */
   5696         sd = _m3d_safestr(model->desc, 3);
   5697         if(!sd) goto memerr;
   5698         /* header */
   5699         h = (m3dhdr_t*)M3D_MALLOC(sizeof(m3dhdr_t) + strlen(sn) + strlen(sl) + strlen(sa) + strlen(sd) + 4);
   5700         if(!h) goto memerr;
   5701         memcpy((uint8_t*)h, "HEAD", 4);
   5702         h->length = sizeof(m3dhdr_t);
   5703         h->scale = scale;
   5704         i = (unsigned int)strlen(sn); memcpy((uint8_t*)h + h->length, sn, i+1); h->length += i+1; M3D_FREE(sn);
   5705         i = (unsigned int)strlen(sl); memcpy((uint8_t*)h + h->length, sl, i+1); h->length += i+1; M3D_FREE(sl);
   5706         i = (unsigned int)strlen(sa); memcpy((uint8_t*)h + h->length, sa, i+1); h->length += i+1; M3D_FREE(sa);
   5707         i = (unsigned int)strlen(sd); memcpy((uint8_t*)h + h->length, sd, i+1); h->length += i+1; M3D_FREE(sd);
   5708         sn = sl = sa = sd = NULL;
   5709         if(model->inlined)
   5710             for(i = 0; i < model->numinlined; i++) {
   5711                 if(model->inlined[i].name && *model->inlined[i].name && model->inlined[i].length > 0) {
   5712                     str = _m3d_addstr(str, &numstr, model->inlined[i].name);
   5713                     if(!str) goto memerr;
   5714                 }
   5715             }
   5716         if(str)
   5717             for(i = 0; i < numstr; i++) {
   5718                 h = _m3d_addhdr(h, &str[i]);
   5719                 if(!h) goto memerr;
   5720             }
   5721         vc_s = quality == M3D_EXP_INT8? 1 : (quality == M3D_EXP_INT16? 2 : (quality == M3D_EXP_DOUBLE? 8 : 4));
   5722         vi_s = maxvrtx < 254 ? 1 : (maxvrtx < 65534 ? 2 : 4);
   5723         si_s = h->length - 16 < 254 ? 1 : (h->length - 16 < 65534 ? 2 : 4);
   5724         ci_s = !numcmap || !cmap ? 0 : (numcmap < 254 ? 1 : (numcmap < 65534 ? 2 : 4));
   5725         ti_s = !maxtmap || !tmap ? 0 : (maxtmap < 254 ? 1 : (maxtmap < 65534 ? 2 : 4));
   5726         bi_s = !model->numbone || !model->bone || (flags & M3D_EXP_NOBONE)? 0 : (model->numbone < 254 ? 1 :
   5727             (model->numbone < 65534 ? 2 : 4));
   5728         nb_s = maxbone < 2 ? 1 : (maxbone == 2 ? 2 : (maxbone <= 4 ? 4 : 8));
   5729         sk_s = !bi_s || !maxskin || !skin ? 0 : (maxskin < 254 ? 1 : (maxskin < 65534 ? 2 : 4));
   5730         fc_s = maxt < 254 ? 1 : (maxt < 65534 ? 2 : 4);
   5731         hi_s = !model->numshape || !model->shape || (flags & M3D_EXP_NOFACE)? 0 : (model->numshape < 254 ? 1 :
   5732             (model->numshape < 65534 ? 2 : 4));
   5733         fi_s = !model->numface || !model->face || (flags & M3D_EXP_NOFACE)? 0 : (model->numface < 254 ? 1 :
   5734             (model->numface < 65534 ? 2 : 4));
   5735         vd_s = !model->numvoxel || !model->voxel || (flags & M3D_EXP_NOFACE)? 0 : (minvox >= -128 && maxvox <= 127 ? 1 :
   5736             (minvox >= -32768 && maxvox <= 32767 ? 2 : 4));
   5737         vp_s = !model->numvoxtype || !model->voxtype || (flags & M3D_EXP_NOFACE)? 0 : (model->numvoxtype < 254 ? 1 :
   5738             (model->numvoxtype < 65534 ? 2 : 4));
   5739         h->types =  (vc_s == 8 ? (3<<0) : (vc_s == 2 ? (1<<0) : (vc_s == 1 ? (0<<0) : (2<<0)))) |
   5740                     (vi_s == 2 ? (1<<2) : (vi_s == 1 ? (0<<2) : (2<<2))) |
   5741                     (si_s == 2 ? (1<<4) : (si_s == 1 ? (0<<4) : (2<<4))) |
   5742                     (ci_s == 2 ? (1<<6) : (ci_s == 1 ? (0<<6) : (ci_s == 4 ? (2<<6) : (3<<6)))) |
   5743                     (ti_s == 2 ? (1<<8) : (ti_s == 1 ? (0<<8) : (ti_s == 4 ? (2<<8) : (3<<8)))) |
   5744                     (bi_s == 2 ? (1<<10): (bi_s == 1 ? (0<<10): (bi_s == 4 ? (2<<10) : (3<<10)))) |
   5745                     (nb_s == 2 ? (1<<12): (nb_s == 1 ? (0<<12): (2<<12))) |
   5746                     (sk_s == 2 ? (1<<14): (sk_s == 1 ? (0<<14): (sk_s == 4 ? (2<<14) : (3<<14)))) |
   5747                     (fc_s == 2 ? (1<<16): (fc_s == 1 ? (0<<16): (2<<16))) |
   5748                     (hi_s == 2 ? (1<<18): (hi_s == 1 ? (0<<18): (hi_s == 4 ? (2<<18) : (3<<18)))) |
   5749                     (fi_s == 2 ? (1<<20): (fi_s == 1 ? (0<<20): (fi_s == 4 ? (2<<20) : (3<<20)))) |
   5750                     (vd_s == 2 ? (1<<22): (vd_s == 1 ? (0<<22): (vd_s == 4 ? (2<<22) : (3<<22)))) |
   5751                     (vp_s == 2 ? (1<<24): (vp_s == 1 ? (0<<24): (vp_s == 4 ? (2<<24) : (3<<24))));
   5752         len = h->length;
   5753         /* color map */
   5754         if(numcmap && cmap && ci_s < 4 && !(flags & M3D_EXP_NOCMAP)) {
   5755             chunklen = 8 + numcmap * sizeof(uint32_t);
   5756             h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   5757             if(!h) goto memerr;
   5758             memcpy((uint8_t*)h + len, "CMAP", 4);
   5759             *((uint32_t*)((uint8_t*)h + len + 4)) = chunklen;
   5760             memcpy((uint8_t*)h + len + 8, cmap, chunklen - 8);
   5761             len += chunklen;
   5762         } else numcmap = 0;
   5763         /* texture map */
   5764         if(numtmap && tmap && !(flags & M3D_EXP_NOTXTCRD) && !(flags & M3D_EXP_NOFACE)) {
   5765             chunklen = 8 + maxtmap * vc_s * 2;
   5766             h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   5767             if(!h) goto memerr;
   5768             memcpy((uint8_t*)h + len, "TMAP", 4);
   5769             length = (uint32_t*)((uint8_t*)h + len + 4);
   5770             out = (uint8_t*)h + len + 8;
   5771             last = M3D_UNDEF;
   5772             for(i = 0; i < numtmap; i++) {
   5773                 if(tmap[i].newidx == last) continue;
   5774                 last = tmap[i].newidx;
   5775                 switch(vc_s) {
   5776                     case 1: *out++ = (uint8_t)(tmap[i].data.u * 255); *out++ = (uint8_t)(tmap[i].data.v * 255); break;
   5777                     case 2:
   5778                         *((uint16_t*)out) = (uint16_t)(tmap[i].data.u * 65535); out += 2;
   5779                         *((uint16_t*)out) = (uint16_t)(tmap[i].data.v * 65535); out += 2;
   5780                     break;
   5781                     case 4:  *((float*)out) = tmap[i].data.u; out += 4;  *((float*)out) = tmap[i].data.v; out += 4; break;
   5782                     case 8: *((double*)out) = tmap[i].data.u; out += 8; *((double*)out) = tmap[i].data.v; out += 8; break;
   5783                 }
   5784             }
   5785             *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
   5786             out = NULL;
   5787             len += *length;
   5788         }
   5789         /* vertex */
   5790         if(numvrtx && vrtx) {
   5791             chunklen = 8 + maxvrtx * (ci_s + sk_s + 4 * vc_s);
   5792             h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   5793             if(!h) goto memerr;
   5794             memcpy((uint8_t*)h + len, "VRTS", 4);
   5795             length = (uint32_t*)((uint8_t*)h + len + 4);
   5796             out = (uint8_t*)h + len + 8;
   5797             last = M3D_UNDEF;
   5798             for(i = 0; i < numvrtx; i++) {
   5799                 if(vrtx[i].newidx == last) continue;
   5800                 last = vrtx[i].newidx;
   5801                 switch(vc_s) {
   5802                     case 1:
   5803                         *out++ = (int8_t)(vrtx[i].data.x * 127);
   5804                         *out++ = (int8_t)(vrtx[i].data.y * 127);
   5805                         *out++ = (int8_t)(vrtx[i].data.z * 127);
   5806                         *out++ = (int8_t)(vrtx[i].data.w * 127);
   5807                     break;
   5808                     case 2:
   5809                         *((int16_t*)out) = (int16_t)(vrtx[i].data.x * 32767); out += 2;
   5810                         *((int16_t*)out) = (int16_t)(vrtx[i].data.y * 32767); out += 2;
   5811                         *((int16_t*)out) = (int16_t)(vrtx[i].data.z * 32767); out += 2;
   5812                         *((int16_t*)out) = (int16_t)(vrtx[i].data.w * 32767); out += 2;
   5813                     break;
   5814                     case 4:
   5815                         *((float*)out) = vrtx[i].data.x; out += 4;
   5816                         *((float*)out) = vrtx[i].data.y; out += 4;
   5817                         *((float*)out) = vrtx[i].data.z; out += 4;
   5818                         *((float*)out) = vrtx[i].data.w; out += 4;
   5819                     break;
   5820                     case 8:
   5821                         *((double*)out) = vrtx[i].data.x; out += 8;
   5822                         *((double*)out) = vrtx[i].data.y; out += 8;
   5823                         *((double*)out) = vrtx[i].data.z; out += 8;
   5824                         *((double*)out) = vrtx[i].data.w; out += 8;
   5825                     break;
   5826                 }
   5827                 idx = _m3d_cmapidx(cmap, numcmap, vrtx[i].data.color);
   5828                 switch(ci_s) {
   5829                     case 1: *out++ = (uint8_t)(idx); break;
   5830                     case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break;
   5831                     case 4: *((uint32_t*)out) = vrtx[i].data.color; out += 4; break;
   5832                 }
   5833                 out = _m3d_addidx(out, sk_s, vrtx[i].data.skinid);
   5834             }
   5835             *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
   5836             out = NULL;
   5837             len += *length;
   5838         }
   5839         /* bones chunk */
   5840         if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) {
   5841             i = 8 + bi_s + sk_s + model->numbone * (bi_s + si_s + 2*vi_s);
   5842             chunklen = i + numskin * nb_s * (bi_s + 1);
   5843             h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   5844             if(!h) goto memerr;
   5845             memcpy((uint8_t*)h + len, "BONE", 4);
   5846             length = (uint32_t*)((uint8_t*)h + len + 4);
   5847             out = (uint8_t*)h + len + 8;
   5848             out = _m3d_addidx(out, bi_s, model->numbone);
   5849             out = _m3d_addidx(out, sk_s, maxskin);
   5850             for(i = 0; i < model->numbone; i++) {
   5851                 out = _m3d_addidx(out, bi_s, model->bone[i].parent);
   5852                 out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->bone[i].name));
   5853                 out = _m3d_addidx(out, vi_s, vrtxidx[model->bone[i].pos]);
   5854                 out = _m3d_addidx(out, vi_s, vrtxidx[model->bone[i].ori]);
   5855             }
   5856             if(numskin && skin && sk_s) {
   5857                 last = M3D_UNDEF;
   5858                 for(i = 0; i < numskin; i++) {
   5859                     if(skin[i].newidx == last) continue;
   5860                     last = skin[i].newidx;
   5861                     memset(&weights, 0, nb_s);
   5862                     for(j = k = l = 0, mw = 0.0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != M3D_UNDEF &&
   5863                         skin[i].data.weight[j] > (M3D_FLOAT)0.0; j++) {
   5864                             if(mw < skin[i].data.weight[j]) { mw = skin[i].data.weight[j]; k = j; }
   5865                             weights[j] = (uint8_t)(skin[i].data.weight[j] * 255);
   5866                             if(!weights[j]) { weights[j]++; l--; }
   5867                         }
   5868                     weights[k] += l;
   5869                     switch(nb_s) {
   5870                         case 1: weights[0] = 255; break;
   5871                         case 2: memcpy(out, weights, 2); out += 2; break;
   5872                         case 4: memcpy(out, weights, 4); out += 4; break;
   5873                         case 8: memcpy(out, weights, 8); out += 8; break;
   5874                     }
   5875                     for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != M3D_UNDEF && weights[j]; j++) {
   5876                         out = _m3d_addidx(out, bi_s, skin[i].data.boneid[j]);
   5877                         *length += bi_s;
   5878                     }
   5879                 }
   5880             }
   5881             *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
   5882             out = NULL;
   5883             len += *length;
   5884         }
   5885         /* materials */
   5886         if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) {
   5887             for(j = 0; j < model->nummaterial; j++) {
   5888                 if(mtrlidx[j] == M3D_UNDEF || !model->material[j].numprop || !model->material[j].prop) continue;
   5889                 m = &model->material[j];
   5890                 chunklen = 12 + si_s + m->numprop * 5;
   5891                 h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   5892                 if(!h) goto memerr;
   5893                 memcpy((uint8_t*)h + len, "MTRL", 4);
   5894                 length = (uint32_t*)((uint8_t*)h + len + 4);
   5895                 out = (uint8_t*)h + len + 8;
   5896                 out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, m->name));
   5897                 for(i = 0; i < m->numprop; i++) {
   5898                     if(m->prop[i].type >= 128) {
   5899                         if(m->prop[i].value.textureid >= model->numtexture ||
   5900                             !model->texture[m->prop[i].value.textureid].name) continue;
   5901                         k = m3dpf_map;
   5902                     } else {
   5903                         for(k = 256, l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++)
   5904                             if(m->prop[i].type == m3d_propertytypes[l].id) { k = m3d_propertytypes[l].format; break; }
   5905                     }
   5906                     if(k == 256) continue;
   5907                     *out++ = m->prop[i].type;
   5908                     switch(k) {
   5909                         case m3dpf_color:
   5910                             if(!(flags & M3D_EXP_NOCMAP)) {
   5911                                 idx = _m3d_cmapidx(cmap, numcmap, m->prop[i].value.color);
   5912                                 switch(ci_s) {
   5913                                     case 1: *out++ = (uint8_t)(idx); break;
   5914                                     case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break;
   5915                                     case 4: *((uint32_t*)out) = (uint32_t)(m->prop[i].value.color); out += 4; break;
   5916                                 }
   5917                             } else out--;
   5918                         break;
   5919                         case m3dpf_uint8:  *out++ = m->prop[i].value.num; break;
   5920                         case m3dpf_uint16: *((uint16_t*)out) = m->prop[i].value.num; out += 2; break;
   5921                         case m3dpf_uint32: *((uint32_t*)out) = m->prop[i].value.num; out += 4; break;
   5922                         case m3dpf_float:  *((float*)out) = m->prop[i].value.fnum; out += 4; break;
   5923 
   5924                         case m3dpf_map:
   5925                             idx = _m3d_stridx(str, numstr, model->texture[m->prop[i].value.textureid].name);
   5926                             out = _m3d_addidx(out, si_s, idx);
   5927                         break;
   5928                     }
   5929                 }
   5930                 *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
   5931                 len += *length;
   5932                 out = NULL;
   5933             }
   5934         }
   5935         /* procedural face */
   5936         if(model->numinlined && model->inlined && !(flags & M3D_EXP_NOFACE)) {
   5937             /* all inlined assets which are not textures should be procedural surfaces */
   5938             for(j = 0; j < model->numinlined; j++) {
   5939                 if(!model->inlined[j].name || !model->inlined[j].name[0] || model->inlined[j].length < 4 ||
   5940                     !model->inlined[j].data || (model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' &&
   5941                     model->inlined[j].data[3] == 'G'))
   5942                     continue;
   5943                 for(i = k = 0; i < model->numtexture; i++) {
   5944                     if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; }
   5945                 }
   5946                 if(k) continue;
   5947                 numproc++;
   5948                 chunklen = 8 + si_s;
   5949                 h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   5950                 if(!h) goto memerr;
   5951                 memcpy((uint8_t*)h + len, "PROC", 4);
   5952                 *((uint32_t*)((uint8_t*)h + len + 4)) = chunklen;
   5953                 out = (uint8_t*)h + len + 8;
   5954                 out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->inlined[j].name));
   5955                 out = NULL;
   5956                 len += chunklen;
   5957             }
   5958         }
   5959         /* mesh face */
   5960         if(model->numface && face && !(flags & M3D_EXP_NOFACE)) {
   5961             chunklen = 8 + si_s + model->numface * (9 * vi_s + 3 * ti_s + si_s + 1);
   5962             h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   5963             if(!h) goto memerr;
   5964             memcpy((uint8_t*)h + len, "MESH", 4);
   5965             length = (uint32_t*)((uint8_t*)h + len + 4);
   5966             out = (uint8_t*)h + len + 8;
   5967             last = M3D_UNDEF;
   5968 #ifdef M3D_VERTEXMAX
   5969             lastp = M3D_UNDEF;
   5970 #endif
   5971             for(i = 0; i < model->numface; i++) {
   5972                 if(!(flags & M3D_EXP_NOMATERIAL) && face[i].data.materialid != last) {
   5973                     last = face[i].data.materialid;
   5974                     idx = last < model->nummaterial ? _m3d_stridx(str, numstr, model->material[last].name) : 0;
   5975                     *out++ = 0;
   5976                     out = _m3d_addidx(out, si_s, idx);
   5977                 }
   5978 #ifdef M3D_VERTEXMAX
   5979                 if(!(flags & M3D_EXP_NOVRTMAX) && face[i].data.paramid != lastp) {
   5980                     lastp = face[i].data.paramid;
   5981                     idx = lastp < model->numparam ? _m3d_stridx(str, numstr, model->param[lastp].name) : 0;
   5982                     *out++ = 0;
   5983                     out = _m3d_addidx(out, si_s, idx);
   5984                 }
   5985 #endif
   5986                 /* hardcoded triangles. */
   5987                 k = (3 << 4) |
   5988                     (((flags & M3D_EXP_NOTXTCRD) || !ti_s || face[i].data.texcoord[0] == M3D_UNDEF ||
   5989                     face[i].data.texcoord[1] == M3D_UNDEF || face[i].data.texcoord[2] == M3D_UNDEF) ? 0 : 1) |
   5990                     (((flags & M3D_EXP_NONORMAL) || face[i].data.normal[0] == M3D_UNDEF ||
   5991                     face[i].data.normal[1] == M3D_UNDEF || face[i].data.normal[2] == M3D_UNDEF) ? 0 : 2)
   5992 #ifdef M3D_VERTEXMAX
   5993                     | (((flags & M3D_EXP_NOVRTMAX) || face[i].data.vertmax[0] == M3D_UNDEF ||
   5994                     face[i].data.vertmax[1] == M3D_UNDEF || face[i].data.vertmax[2] == M3D_UNDEF) ? 0 : 4)
   5995 #endif
   5996                     ;
   5997                 *out++ = k;
   5998                 for(j = 0; j < 3; j++) {
   5999                     out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.vertex[j]]);
   6000                     if(k & 1)
   6001                         out = _m3d_addidx(out, ti_s, tmapidx[face[i].data.texcoord[j]]);
   6002                     if(k & 2)
   6003                         out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.normal[j]]);
   6004 #ifdef M3D_VERTEXMAX
   6005                     if(k & 4)
   6006                         out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.vertmax[j]]);
   6007 #endif
   6008                 }
   6009             }
   6010             *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
   6011             len += *length;
   6012             out = NULL;
   6013         }
   6014         /* voxel face */
   6015         if(model->numvoxtype && model->voxtype && !(flags & M3D_EXP_NOFACE)) {
   6016             chunklen = 8 + si_s + model->numvoxtype * (ci_s + si_s + 3 + sk_s);
   6017             for(i = 0; i < model->numvoxtype; i++)
   6018                 chunklen += model->voxtype[i].numitem * (2 + si_s);
   6019             h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   6020             if(!h) goto memerr;
   6021             memcpy((uint8_t*)h + len, "VOXT", 4);
   6022             length = (uint32_t*)((uint8_t*)h + len + 4);
   6023             out = (uint8_t*)h + len + 8;
   6024             for(i = 0; i < model->numvoxtype; i++) {
   6025                 if(!(flags & M3D_EXP_NOCMAP)) {
   6026                     idx = _m3d_cmapidx(cmap, numcmap, model->voxtype[i].color);
   6027                     switch(ci_s) {
   6028                         case 1: *out++ = (uint8_t)(idx); break;
   6029                         case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break;
   6030                         case 4: *((uint32_t*)out) = (uint32_t)(model->voxtype[i].color); out += 4; break;
   6031                     }
   6032                 }
   6033                 out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxtype[i].name));
   6034                 *out++ = (model->voxtype[i].rotation & 0xBF) | (((model->voxtype[i].voxshape >> 8) & 1) << 6);
   6035                 *out++ = model->voxtype[i].voxshape;
   6036                 *out++ = model->voxtype[i].numitem;
   6037                 if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin)
   6038                     out = _m3d_addidx(out, sk_s, skinidx[model->voxtype[i].skinid]);
   6039                 for(j = 0; j < model->voxtype[i].numitem; j++) {
   6040                     out = _m3d_addidx(out, 2, model->voxtype[i].item[j].count);
   6041                     out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxtype[i].item[j].name));
   6042                 }
   6043             }
   6044             *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
   6045             len += *length;
   6046             out = NULL;
   6047         }
   6048         if(model->numvoxel && model->voxel && !(flags & M3D_EXP_NOFACE)) {
   6049             for(j = 0; j < model->numvoxel; j++) {
   6050                 chunklen = 8 + si_s + 6 * vd_s + 2 + model->voxel[j].w * model->voxel[j].h * model->voxel[j].d * 3;
   6051                 h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   6052                 if(!h) goto memerr;
   6053                 memcpy((uint8_t*)h + len, "VOXD", 4);
   6054                 length = (uint32_t*)((uint8_t*)h + len + 4);
   6055                 out = (uint8_t*)h + len + 8;
   6056                 out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxel[j].name));
   6057                 out = _m3d_addidx(out, vd_s, model->voxel[j].x);
   6058                 out = _m3d_addidx(out, vd_s, model->voxel[j].y);
   6059                 out = _m3d_addidx(out, vd_s, model->voxel[j].z);
   6060                 out = _m3d_addidx(out, vd_s, model->voxel[j].w);
   6061                 out = _m3d_addidx(out, vd_s, model->voxel[j].h);
   6062                 out = _m3d_addidx(out, vd_s, model->voxel[j].d);
   6063                 *out++ = model->voxel[j].uncertain;
   6064                 *out++ = model->voxel[j].groupid;
   6065                 /* RLE compress voxel data */
   6066                 n = model->voxel[j].w * model->voxel[j].h * model->voxel[j].d;
   6067                 k = o = 0; out[o++] = 0;
   6068                 for(i = 0; i < n; i++) {
   6069                     for(l = 1; l < 128 && i + l < n && model->voxel[j].data[i] == model->voxel[j].data[i + l]; l++);
   6070                     if(l > 1) {
   6071                         l--;
   6072                         if(out[k]) { out[k]--; out[o++] = 0x80 | l; }
   6073                         else out[k] = 0x80 | l;
   6074                         switch(vp_s) {
   6075                             case 1: out[o++] = model->voxel[j].data[i]; break;
   6076                             default: *((uint16_t*)(out + o)) = model->voxel[j].data[i]; o += 2; break;
   6077                         }
   6078                         k = o; out[o++] = 0;
   6079                         i += l;
   6080                         continue;
   6081                     }
   6082                     out[k]++;
   6083                     switch(vp_s) {
   6084                         case 1: out[o++] = model->voxel[j].data[i]; break;
   6085                         default: *((uint16_t*)(out + o)) = model->voxel[j].data[i]; o += 2; break;
   6086                     }
   6087                     if(out[k] > 127) { out[k]--; k = o; out[o++] = 0; }
   6088                 }
   6089                 if(!(out[k] & 0x80)) { if(out[k]) out[k]--; else o--; }
   6090                 *length = (uint32_t)((uintptr_t)out + (uintptr_t)o - (uintptr_t)((uint8_t*)h + len));
   6091                 len += *length;
   6092                 out = NULL;
   6093             }
   6094         }
   6095         /* mathematical shapes face */
   6096         if(model->numshape && model->shape && !(flags & M3D_EXP_NOFACE)) {
   6097             for(j = 0; j < model->numshape; j++) {
   6098                 chunklen = 12 + si_s + model->shape[j].numcmd * (M3D_CMDMAXARG + 1) * 4;
   6099                 h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   6100                 if(!h) goto memerr;
   6101                 memcpy((uint8_t*)h + len, "SHPE", 4);
   6102                 length = (uint32_t*)((uint8_t*)h + len + 4);
   6103                 out = (uint8_t*)h + len + 8;
   6104                 out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->shape[j].name));
   6105                 out = _m3d_addidx(out, bi_s, model->shape[j].group);
   6106                 for(i = 0; i < model->shape[j].numcmd; i++) {
   6107                     cmd = &model->shape[j].cmd[i];
   6108                     if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg)
   6109                         continue;
   6110                     cd = &m3d_commandtypes[cmd->type];
   6111                     *out++ = (cmd->type & 0x7F) | (cmd->type > 127 ? 0x80 : 0);
   6112                     if(cmd->type > 127) *out++ = (cmd->type >> 7) & 0xff;
   6113                     for(k = n = 0, l = cd->p; k < l; k++) {
   6114                         switch(cd->a[((k - n) % (cd->p - n)) + n]) {
   6115                             case m3dcp_mi_t:
   6116                                 out = _m3d_addidx(out, si_s, cmd->arg[k] < model->nummaterial ?
   6117                                     _m3d_stridx(str, numstr, model->material[cmd->arg[k]].name) : 0);
   6118                             break;
   6119                             case m3dcp_vc_t:
   6120                                 min_x = *((float*)&cmd->arg[k]);
   6121                                 switch(vc_s) {
   6122                                     case 1: *out++ = (int8_t)(min_x * 127); break;
   6123                                     case 2: *((int16_t*)out) = (int16_t)(min_x * 32767); out += 2; break;
   6124                                     case 4: *((float*)out) = min_x; out += 4; break;
   6125                                     case 8: *((double*)out) = min_x; out += 8; break;
   6126                                 }
   6127                             break;
   6128                             case m3dcp_hi_t: out = _m3d_addidx(out, hi_s, cmd->arg[k]); break;
   6129                             case m3dcp_fi_t: out = _m3d_addidx(out, fi_s, cmd->arg[k]); break;
   6130                             case m3dcp_ti_t: out = _m3d_addidx(out, ti_s, cmd->arg[k]); break;
   6131                             case m3dcp_qi_t:
   6132                             case m3dcp_vi_t: out = _m3d_addidx(out, vi_s, cmd->arg[k]); break;
   6133                             case m3dcp_i1_t: out = _m3d_addidx(out, 1, cmd->arg[k]); break;
   6134                             case m3dcp_i2_t: out = _m3d_addidx(out, 2, cmd->arg[k]); break;
   6135                             case m3dcp_i4_t: out = _m3d_addidx(out, 4, cmd->arg[k]); break;
   6136                             case m3dcp_va_t: out = _m3d_addidx(out, 4, cmd->arg[k]);
   6137                                 n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1);
   6138                             break;
   6139                         }
   6140                     }
   6141                 }
   6142                 *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
   6143                 len += *length;
   6144                 out = NULL;
   6145             }
   6146         }
   6147         /* annotation labels */
   6148         if(model->numlabel && model->label) {
   6149             for(i = 0, length = NULL; i < model->numlabel; i++) {
   6150                 if(!i || _m3d_strcmp(sl, model->label[i].lang) || _m3d_strcmp(sn, model->label[i].name)) {
   6151                     sl = model->label[i].lang;
   6152                     sn = model->label[i].name;
   6153                     if(length) {
   6154                         *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
   6155                         len += *length;
   6156                     }
   6157                     chunklen = 8 + 2 * si_s + ci_s + model->numlabel * (vi_s + si_s);
   6158                     h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   6159                     if(!h) { sn = NULL; sl = NULL; goto memerr; }
   6160                     memcpy((uint8_t*)h + len, "LBLS", 4);
   6161                     length = (uint32_t*)((uint8_t*)h + len + 4);
   6162                     out = (uint8_t*)h + len + 8;
   6163                     out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].name));
   6164                     out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].lang));
   6165                     idx = _m3d_cmapidx(cmap, numcmap, model->label[i].color);
   6166                     switch(ci_s) {
   6167                         case 1: *out++ = (uint8_t)(idx); break;
   6168                         case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break;
   6169                         case 4: *((uint32_t*)out) = model->label[i].color; out += 4; break;
   6170                     }
   6171                 }
   6172                 out = _m3d_addidx(out, vi_s, vrtxidx[model->label[i].vertexid]);
   6173                 out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].text));
   6174             }
   6175             if(length) {
   6176                 *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
   6177                 len += *length;
   6178             }
   6179             out = NULL;
   6180             sn = sl = NULL;
   6181         }
   6182         /* actions */
   6183         if(model->numaction && model->action && model->numbone && model->bone && !(flags & M3D_EXP_NOACTION)) {
   6184             for(j = 0; j < model->numaction; j++) {
   6185                 a = &model->action[j];
   6186                 chunklen = 14 + si_s + a->numframe * (4 + fc_s + maxt * (bi_s + 2 * vi_s));
   6187                 h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   6188                 if(!h) goto memerr;
   6189                 memcpy((uint8_t*)h + len, "ACTN", 4);
   6190                 length = (uint32_t*)((uint8_t*)h + len + 4);
   6191                 out = (uint8_t*)h + len + 8;
   6192                 out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, a->name));
   6193                 *((uint16_t*)out) = (uint16_t)(a->numframe); out += 2;
   6194                 *((uint32_t*)out) = (uint32_t)(a->durationmsec); out += 4;
   6195                 for(i = 0; i < a->numframe; i++) {
   6196                     *((uint32_t*)out) = (uint32_t)(a->frame[i].msec); out += 4;
   6197                     out = _m3d_addidx(out, fc_s, a->frame[i].numtransform);
   6198                     for(k = 0; k < a->frame[i].numtransform; k++) {
   6199                         out = _m3d_addidx(out, bi_s, a->frame[i].transform[k].boneid);
   6200                         out = _m3d_addidx(out, vi_s, vrtxidx[a->frame[i].transform[k].pos]);
   6201                         out = _m3d_addidx(out, vi_s, vrtxidx[a->frame[i].transform[k].ori]);
   6202                     }
   6203                 }
   6204                 *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
   6205                 len += *length;
   6206                 out = NULL;
   6207             }
   6208         }
   6209         /* inlined assets */
   6210         if(model->numinlined && model->inlined && (numproc || (flags & M3D_EXP_INLINE))) {
   6211             for(j = 0; j < model->numinlined; j++) {
   6212                 if(!model->inlined[j].name || !model->inlined[j].name[0] || model->inlined[j].length<4 || !model->inlined[j].data)
   6213                     continue;
   6214                 if(!(flags & M3D_EXP_INLINE)) {
   6215                     if(model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' && model->inlined[j].data[3] == 'G')
   6216                         continue;
   6217                     for(i = k = 0; i < model->numtexture; i++) {
   6218                         if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; }
   6219                     }
   6220                     if(k) continue;
   6221                 }
   6222                 chunklen = 8 + si_s + model->inlined[j].length;
   6223                 h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   6224                 if(!h) goto memerr;
   6225                 memcpy((uint8_t*)h + len, "ASET", 4);
   6226                 *((uint32_t*)((uint8_t*)h + len + 4)) = chunklen;
   6227                 out = (uint8_t*)h + len + 8;
   6228                 out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->inlined[j].name));
   6229                 memcpy(out, model->inlined[j].data, model->inlined[j].length);
   6230                 out = NULL;
   6231                 len += chunklen;
   6232             }
   6233         }
   6234         /* extra chunks */
   6235         if(model->numextra && model->extra && (flags & M3D_EXP_EXTRA)) {
   6236             for(j = 0; j < model->numextra; j++) {
   6237                 if(!model->extra[j] || model->extra[j]->length < 8)
   6238                     continue;
   6239                 chunklen = model->extra[j]->length;
   6240                 h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
   6241                 if(!h) goto memerr;
   6242                 memcpy((uint8_t*)h + len, model->extra[j], chunklen);
   6243                 len += chunklen;
   6244             }
   6245         }
   6246         /* add end chunk */
   6247         h = (m3dhdr_t*)M3D_REALLOC(h, len + 4);
   6248         if(!h) goto memerr;
   6249         memcpy((uint8_t*)h + len, "OMD3", 4);
   6250         len += 4;
   6251         /* zlib compress */
   6252         if(!(flags & M3D_EXP_NOZLIB)) {
   6253             M3D_LOG("Deflating chunks");
   6254             z = stbi_zlib_compress((unsigned char *)h, len, (int*)&l, 9);
   6255             if(z && l > 0 && l < len) { len = l; M3D_FREE(h); h = (m3dhdr_t*)z; }
   6256         }
   6257         /* add file header at the begining */
   6258         len += 8;
   6259         out = (unsigned char*)M3D_MALLOC(len);
   6260         if(!out) goto memerr;
   6261         memcpy(out, "3DMO", 4);
   6262         *((uint32_t*)(out + 4)) = len;
   6263         /* preview image chunk, must be the first if exists */
   6264         if(model->preview.data && model->preview.length) {
   6265             chunklen = 8 + model->preview.length;
   6266             out = (unsigned char*)M3D_REALLOC(out, len + chunklen);
   6267             if(!out) goto memerr;
   6268             memcpy((uint8_t*)out + 8, "PRVW", 4);
   6269             *((uint32_t*)((uint8_t*)out + 8 + 4)) = chunklen;
   6270             memcpy((uint8_t*)out + 8 + 8, model->preview.data, model->preview.length);
   6271             *((uint32_t*)(out + 4)) += chunklen;
   6272         } else
   6273             chunklen = 0;
   6274         memcpy(out + 8 + chunklen, h, len - 8);
   6275     }
   6276     if(size) *size = out ? len : 0;
   6277     if(vrtxidx) M3D_FREE(vrtxidx);
   6278     if(mtrlidx) M3D_FREE(mtrlidx);
   6279     if(tmapidx) M3D_FREE(tmapidx);
   6280     if(skinidx) M3D_FREE(skinidx);
   6281     if(norm) M3D_FREE(norm);
   6282     if(face) M3D_FREE(face);
   6283     if(cmap) M3D_FREE(cmap);
   6284     if(tmap) M3D_FREE(tmap);
   6285     if(skin) M3D_FREE(skin);
   6286     if(str) M3D_FREE(str);
   6287     if(vrtx) M3D_FREE(vrtx);
   6288     if(opa) free(opa);
   6289     if(h) M3D_FREE(h);
   6290     return out;
   6291 }
   6292 #endif
   6293 
   6294 #endif
   6295 
   6296 #ifdef  __cplusplus
   6297 }
   6298 #ifdef M3D_CPPWRAPPER
   6299 #include <vector>
   6300 #include <string>
   6301 #include <memory>
   6302 
   6303 /*** C++ wrapper class ***/
   6304 namespace M3D {
   6305 #ifdef M3D_IMPLEMENTATION
   6306 
   6307     class Model {
   6308         public:
   6309             m3d_t *model;
   6310 
   6311         public:
   6312             Model() {
   6313                 this->model = (m3d_t*)malloc(sizeof(m3d_t)); memset(this->model, 0, sizeof(m3d_t));
   6314             }
   6315             Model(_unused const std::string &data, _unused m3dread_t ReadFileCB,
   6316                 _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) {
   6317 #ifndef M3D_NOIMPORTER
   6318                 this->model = m3d_load((unsigned char *)data.data(), ReadFileCB, FreeCB, mtllib.model);
   6319 #else
   6320                 Model();
   6321 #endif
   6322             }
   6323             Model(_unused const std::vector<unsigned char> data, _unused m3dread_t ReadFileCB,
   6324                 _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) {
   6325 #ifndef M3D_NOIMPORTER
   6326                 this->model = m3d_load((unsigned char *)&data[0], ReadFileCB, FreeCB, mtllib.model);
   6327 #else
   6328                 Model();
   6329 #endif
   6330             }
   6331             Model(_unused const unsigned char *data, _unused m3dread_t ReadFileCB,
   6332                 _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) {
   6333 #ifndef M3D_NOIMPORTER
   6334                 this->model = m3d_load((unsigned char*)data, ReadFileCB, FreeCB, mtllib.model);
   6335 #else
   6336                 Model();
   6337 #endif
   6338             }
   6339             ~Model() { m3d_free(this->model); }
   6340 
   6341         public:
   6342             m3d_t *getCStruct() { return this->model; }
   6343             std::string getName() { return std::string(this->model->name); }
   6344             void setName(std::string name) { this->model->name = (char*)name.c_str(); }
   6345             std::string getLicense() { return std::string(this->model->license); }
   6346             void setLicense(std::string license) { this->model->license = (char*)license.c_str(); }
   6347             std::string getAuthor() { return std::string(this->model->author); }
   6348             void setAuthor(std::string author) { this->model->author = (char*)author.c_str(); }
   6349             std::string getDescription() { return std::string(this->model->desc); }
   6350             void setDescription(std::string desc) { this->model->desc = (char*)desc.c_str(); }
   6351             float getScale() { return this->model->scale; }
   6352             void setScale(float scale) { this->model->scale = scale; }
   6353             std::vector<unsigned char> getPreview() { return this->model->preview.data ?
   6354                 std::vector<unsigned char>(this->model->preview.data, this->model->preview.data + this->model->preview.length) :
   6355                 std::vector<unsigned char>(); }
   6356             std::vector<uint32_t> getColorMap() { return this->model->cmap ? std::vector<uint32_t>(this->model->cmap,
   6357                 this->model->cmap + this->model->numcmap) : std::vector<uint32_t>(); }
   6358             std::vector<m3dti_t> getTextureMap() { return this->model->tmap ? std::vector<m3dti_t>(this->model->tmap,
   6359                 this->model->tmap + this->model->numtmap) : std::vector<m3dti_t>(); }
   6360             std::vector<m3dtx_t> getTextures() { return this->model->texture ? std::vector<m3dtx_t>(this->model->texture,
   6361                 this->model->texture + this->model->numtexture) : std::vector<m3dtx_t>(); }
   6362             std::string getTextureName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numtexture ?
   6363                 std::string(this->model->texture[idx].name) : nullptr; }
   6364             std::vector<m3db_t> getBones() { return this->model->bone ? std::vector<m3db_t>(this->model->bone, this->model->bone +
   6365                 this->model->numbone) : std::vector<m3db_t>(); }
   6366             std::string getBoneName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numbone ?
   6367                 std::string(this->model->bone[idx].name) : nullptr; }
   6368             std::vector<m3dm_t> getMaterials() { return this->model->material ? std::vector<m3dm_t>(this->model->material,
   6369                 this->model->material + this->model->nummaterial) : std::vector<m3dm_t>(); }
   6370             std::string getMaterialName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->nummaterial ?
   6371                 std::string(this->model->material[idx].name) : nullptr; }
   6372             int getMaterialPropertyInt(int idx, int type) {
   6373                     if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 0 || type >= 127 ||
   6374                         !this->model->material[idx].prop) return -1;
   6375                     for (int i = 0; i < this->model->material[idx].numprop; i++) {
   6376                         if (this->model->material[idx].prop[i].type == type)
   6377                             return this->model->material[idx].prop[i].value.num;
   6378                     }
   6379                     return -1;
   6380                 }
   6381             uint32_t getMaterialPropertyColor(int idx, int type) { return this->getMaterialPropertyInt(idx, type); }
   6382             float getMaterialPropertyFloat(int idx, int type) {
   6383                     if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 0 || type >= 127 ||
   6384                         !this->model->material[idx].prop) return -1.0f;
   6385                     for (int i = 0; i < this->model->material[idx].numprop; i++) {
   6386                         if (this->model->material[idx].prop[i].type == type)
   6387                             return this->model->material[idx].prop[i].value.fnum;
   6388                     }
   6389                     return -1.0f;
   6390                 }
   6391             m3dtx_t* getMaterialPropertyMap(int idx, int type) {
   6392                     if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 128 || type > 255 ||
   6393                         !this->model->material[idx].prop) return nullptr;
   6394                     for (int i = 0; i < this->model->material[idx].numprop; i++) {
   6395                         if (this->model->material[idx].prop[i].type == type)
   6396                             return this->model->material[idx].prop[i].value.textureid < this->model->numtexture ?
   6397                                 &this->model->texture[this->model->material[idx].prop[i].value.textureid] : nullptr;
   6398                     }
   6399                     return nullptr;
   6400                 }
   6401             std::vector<m3dv_t> getVertices() { return this->model->vertex ? std::vector<m3dv_t>(this->model->vertex,
   6402                 this->model->vertex + this->model->numvertex) : std::vector<m3dv_t>(); }
   6403             std::vector<m3df_t> getFace() { return this->model->face ? std::vector<m3df_t>(this->model->face, this->model->face +
   6404                 this->model->numface) : std::vector<m3df_t>(); }
   6405             std::vector<m3dvt_t> getVoxelTypes() { return this->model->voxtype ? std::vector<m3dvt_t>(this->model->voxtype,
   6406                 this->model->voxtype + this->model->numvoxtype) : std::vector<m3dvt_t>(); }
   6407             std::string getVoxelTypeName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxtype &&
   6408                 this->model->voxtype[idx].name && this->model->voxtype[idx].name[0] ?
   6409                 std::string(this->model->voxtype[idx].name) : nullptr; }
   6410             std::vector<m3dvi_t> getVoxelTypeItems(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxtype &&
   6411                 this->model->voxtype[idx].item ? std::vector<m3dvi_t>(this->model->voxtype[idx].item,
   6412                 this->model->voxtype[idx].item + this->model->voxtype[idx].numitem) : std::vector<m3dvi_t>(); }
   6413             std::vector<m3dvx_t> getVoxelBlocks() { return this->model->voxel ? std::vector<m3dvx_t>(this->model->voxel,
   6414                 this->model->voxel + this->model->numvoxel) : std::vector<m3dvx_t>(); }
   6415             std::string getVoxelBlockName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxel &&
   6416                 this->model->voxel[idx].name && this->model->voxel[idx].name[0] ?
   6417                 std::string(this->model->voxel[idx].name) : nullptr; }
   6418             std::vector<M3D_VOXEL> getVoxelBlockData(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxel &&
   6419                 this->model->voxel[idx].data ? std::vector<M3D_VOXEL>(this->model->voxel[idx].data,
   6420                 this->model->voxel[idx].data + this->model->voxel[idx].w*this->model->voxel[idx].h*this->model->voxel[idx].d) :
   6421                 std::vector<M3D_VOXEL>(); }
   6422             std::vector<m3dh_t> getShape() { return this->model->shape ? std::vector<m3dh_t>(this->model->shape,
   6423                 this->model->shape + this->model->numshape) : std::vector<m3dh_t>(); }
   6424             std::string getShapeName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape &&
   6425                 this->model->shape[idx].name && this->model->shape[idx].name[0] ?
   6426                 std::string(this->model->shape[idx].name) : nullptr; }
   6427             unsigned int getShapeGroup(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape ?
   6428                 this->model->shape[idx].group : 0xFFFFFFFF; }
   6429             std::vector<m3dc_t> getShapeCommands(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape &&
   6430                 this->model->shape[idx].cmd ? std::vector<m3dc_t>(this->model->shape[idx].cmd, this->model->shape[idx].cmd +
   6431                 this->model->shape[idx].numcmd) : std::vector<m3dc_t>(); }
   6432             std::vector<m3dl_t> getAnnotationLabels() { return this->model->label ? std::vector<m3dl_t>(this->model->label,
   6433                 this->model->label + this->model->numlabel) : std::vector<m3dl_t>(); }
   6434             std::vector<m3ds_t> getSkin() { return this->model->skin ? std::vector<m3ds_t>(this->model->skin, this->model->skin +
   6435                 this->model->numskin) : std::vector<m3ds_t>(); }
   6436             std::vector<m3da_t> getActions() { return this->model->action ? std::vector<m3da_t>(this->model->action,
   6437                 this->model->action + this->model->numaction) : std::vector<m3da_t>(); }
   6438             std::string getActionName(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ?
   6439                 std::string(this->model->action[aidx].name) : nullptr; }
   6440             unsigned int getActionDuration(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ?
   6441                 this->model->action[aidx].durationmsec : 0; }
   6442             std::vector<m3dfr_t> getActionFrames(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ?
   6443                 std::vector<m3dfr_t>(this->model->action[aidx].frame, this->model->action[aidx].frame +
   6444                 this->model->action[aidx].numframe) : std::vector<m3dfr_t>(); }
   6445             unsigned int getActionFrameTimestamp(int aidx, int fidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction?
   6446                     (fidx >= 0 && (unsigned int)fidx < this->model->action[aidx].numframe ?
   6447                     this->model->action[aidx].frame[fidx].msec : 0) : 0; }
   6448             std::vector<m3dtr_t> getActionFrameTransforms(int aidx, int fidx) {
   6449                 return aidx >= 0 && (unsigned int)aidx < this->model->numaction ? (
   6450                     fidx >= 0 && (unsigned int)fidx < this->model->action[aidx].numframe ?
   6451                     std::vector<m3dtr_t>(this->model->action[aidx].frame[fidx].transform,
   6452                     this->model->action[aidx].frame[fidx].transform + this->model->action[aidx].frame[fidx].numtransform) :
   6453                     std::vector<m3dtr_t>()) : std::vector<m3dtr_t>(); }
   6454             std::vector<m3dtr_t> getActionFrame(int aidx, int fidx, std::vector<m3dtr_t> skeleton) {
   6455                 m3dtr_t *pose = m3d_frame(this->model, (unsigned int)aidx, (unsigned int)fidx,
   6456                     skeleton.size() ? &skeleton[0] : nullptr);
   6457                 return std::vector<m3dtr_t>(pose, pose + this->model->numbone); }
   6458             std::vector<m3db_t> getActionPose(int aidx, unsigned int msec) {
   6459                 m3db_t *pose = m3d_pose(this->model, (unsigned int)aidx, (unsigned int)msec);
   6460                 return std::vector<m3db_t>(pose, pose + this->model->numbone); }
   6461             std::vector<m3di_t> getInlinedAssets() { return this->model->inlined ? std::vector<m3di_t>(this->model->inlined,
   6462                 this->model->inlined + this->model->numinlined) : std::vector<m3di_t>(); }
   6463             std::vector<std::unique_ptr<m3dchunk_t>> getExtras() { return this->model->extra ?
   6464                 std::vector<std::unique_ptr<m3dchunk_t>>(this->model->extra,
   6465                 this->model->extra + this->model->numextra) : std::vector<std::unique_ptr<m3dchunk_t>>(); }
   6466             std::vector<unsigned char> Save(_unused int quality, _unused int flags) {
   6467 #ifdef M3D_EXPORTER
   6468                 unsigned int size;
   6469                 unsigned char *ptr = m3d_save(this->model, quality, flags, &size);
   6470                 return ptr && size ? std::vector<unsigned char>(ptr, ptr + size) : std::vector<unsigned char>();
   6471 #else
   6472                 return std::vector<unsigned char>();
   6473 #endif
   6474             }
   6475     };
   6476 
   6477 #else
   6478     class Model {
   6479         private:
   6480             m3d_t *model;
   6481 
   6482         public:
   6483             Model(const std::string &data, m3dread_t ReadFileCB, m3dfree_t FreeCB);
   6484             Model(const std::vector<unsigned char> data, m3dread_t ReadFileCB, m3dfree_t FreeCB);
   6485             Model(const unsigned char *data, m3dread_t ReadFileCB, m3dfree_t FreeCB);
   6486             Model();
   6487             ~Model();
   6488 
   6489         public:
   6490             m3d_t *getCStruct();
   6491             std::string getName();
   6492             void setName(std::string name);
   6493             std::string getLicense();
   6494             void setLicense(std::string license);
   6495             std::string getAuthor();
   6496             void setAuthor(std::string author);
   6497             std::string getDescription();
   6498             void setDescription(std::string desc);
   6499             float getScale();
   6500             void setScale(float scale);
   6501             std::vector<unsigned char> getPreview();
   6502             std::vector<uint32_t> getColorMap();
   6503             std::vector<m3dti_t> getTextureMap();
   6504             std::vector<m3dtx_t> getTextures();
   6505             std::string getTextureName(int idx);
   6506             std::vector<m3db_t> getBones();
   6507             std::string getBoneName(int idx);
   6508             std::vector<m3dm_t> getMaterials();
   6509             std::string getMaterialName(int idx);
   6510             int getMaterialPropertyInt(int idx, int type);
   6511             uint32_t getMaterialPropertyColor(int idx, int type);
   6512             float getMaterialPropertyFloat(int idx, int type);
   6513             m3dtx_t* getMaterialPropertyMap(int idx, int type);
   6514             std::vector<m3dv_t> getVertices();
   6515             std::vector<m3df_t> getFace();
   6516             std::vector<m3dvt_t> getVoxelTypes();
   6517             std::string getVoxelTypeName(int idx);
   6518             std::vector<m3dvi_t> getVoxelTypeItems(int idx);
   6519             std::vector<m3dvx_t> getVoxelBlocks();
   6520             std::string getVoxelBlockName(int idx);
   6521             std::vector<M3D_VOXEL> getVoxelBlockData(int idx);
   6522             std::vector<m3dh_t> getShape();
   6523             std::string getShapeName(int idx);
   6524             unsigned int getShapeGroup(int idx);
   6525             std::vector<m3dc_t> getShapeCommands(int idx);
   6526             std::vector<m3dl_t> getAnnotationLabels();
   6527             std::vector<m3ds_t> getSkin();
   6528             std::vector<m3da_t> getActions();
   6529             std::string getActionName(int aidx);
   6530             unsigned int getActionDuration(int aidx);
   6531             std::vector<m3dfr_t> getActionFrames(int aidx);
   6532             unsigned int getActionFrameTimestamp(int aidx, int fidx);
   6533             std::vector<m3dtr_t> getActionFrameTransforms(int aidx, int fidx);
   6534             std::vector<m3dtr_t> getActionFrame(int aidx, int fidx, std::vector<m3dtr_t> skeleton);
   6535             std::vector<m3db_t> getActionPose(int aidx, unsigned int msec);
   6536             std::vector<m3di_t> getInlinedAssets();
   6537             std::vector<std::unique_ptr<m3dchunk_t>> getExtras();
   6538             std::vector<unsigned char> Save(int quality, int flags);
   6539     };
   6540 
   6541 #endif /* impl */
   6542 }
   6543 #endif
   6544 
   6545 #endif /* __cplusplus */
   6546 
   6547 #endif