github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/tests/raylib/external/m3d.h (about)

     1  /*
     2   * m3d.h
     3   * https://gitlab.com/bztsrc/model3d
     4   *
     5   * Copyright (C) 2020 bzt (bztsrc@gitlab)
     6   *
     7   * Permission is hereby granted, free of charge, to any person
     8   * obtaining a copy of this software and associated documentation
     9   * files (the "Software"), to deal in the Software without
    10   * restriction, including without limitation the rights to use, copy,
    11   * modify, merge, publish, distribute, sublicense, and/or sell copies
    12   * of the Software, and to permit persons to whom the Software is
    13   * furnished to do so, subject to the following conditions:
    14   *
    15   * The above copyright notice and this permission notice shall be
    16   * included in all copies or substantial portions of the Software.
    17   *
    18   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    19   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    20   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    21   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    22   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    23   * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    24   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    25   * DEALINGS IN THE SOFTWARE.
    26   *
    27   * @brief ANSI C89 / C++11 single header importer / exporter SDK for the Model 3D (.M3D) format
    28   * https://gitlab.com/bztsrc/model3d
    29   *
    30   * PNG decompressor included from (with minor modifications to make it C89 valid):
    31   *  stb_image - v2.13 - public domain image loader - http://nothings.org/stb_image.h
    32   *
    33   * @version: 1.0.0
    34   */
    35  
    36  #ifndef _M3D_H_
    37  #define _M3D_H_
    38  
    39  #ifdef  __cplusplus
    40  extern "C" {
    41  #endif
    42  
    43  #include <stdint.h>
    44  
    45  /*** configuration ***/
    46  #ifndef M3D_MALLOC
    47  # define M3D_MALLOC(sz)     malloc(sz)
    48  #endif
    49  #ifndef M3D_REALLOC
    50  # define M3D_REALLOC(p,nsz) realloc(p,nsz)
    51  #endif
    52  #ifndef M3D_FREE
    53  # define M3D_FREE(p)        free(p)
    54  #endif
    55  #ifndef M3D_LOG
    56  # define M3D_LOG(x)
    57  #endif
    58  #ifndef M3D_APIVERSION
    59  #define M3D_APIVERSION      0x0100
    60  #ifndef M3D_DOUBLE
    61  typedef float M3D_FLOAT;
    62  #ifndef M3D_EPSILON
    63  /* carefully choosen for IEEE 754 don't change */
    64  #define M3D_EPSILON ((M3D_FLOAT)1e-7)
    65  #endif
    66  #else
    67  typedef double M3D_FLOAT;
    68  #ifndef M3D_EPSILON
    69  #define M3D_EPSILON ((M3D_FLOAT)1e-14)
    70  #endif
    71  #endif
    72  #if !defined(M3D_SMALLINDEX)
    73  typedef uint32_t M3D_INDEX;
    74  typedef uint16_t M3D_VOXEL;
    75  #define M3D_UNDEF 0xffffffff
    76  #define M3D_INDEXMAX 0xfffffffe
    77  #define M3D_VOXUNDEF 0xffff
    78  #define M3D_VOXCLEAR 0xfffe
    79  #else
    80  typedef uint16_t M3D_INDEX;
    81  typedef uint8_t M3D_VOXEL;
    82  #define M3D_UNDEF 0xffff
    83  #define M3D_INDEXMAX 0xfffe
    84  #define M3D_VOXUNDEF 0xff
    85  #define M3D_VOXCLEAR 0xfe
    86  #endif
    87  #define M3D_NOTDEFINED 0xffffffff
    88  #ifndef M3D_NUMBONE
    89  #define M3D_NUMBONE 4
    90  #endif
    91  #ifndef M3D_BONEMAXLEVEL
    92  #define M3D_BONEMAXLEVEL 8
    93  #endif
    94  #ifndef _MSC_VER
    95  #ifndef _inline
    96  #define _inline __inline__
    97  #endif
    98  #define _pack __attribute__((packed))
    99  #define _unused __attribute__((unused))
   100  #else
   101  #define _inline
   102  #define _pack
   103  #define _unused __pragma(warning(suppress:4100))
   104  #endif
   105  #ifndef  __cplusplus
   106  #define _register register
   107  #else
   108  #define _register
   109  #endif
   110  
   111  /*** File format structures ***/
   112  
   113  /**
   114   * M3D file format structure
   115   *  3DMO m3dchunk_t file header chunk, may followed by compressed data
   116   *  PRVW preview chunk (optional)
   117   *  HEAD m3dhdr_t model header chunk
   118   *  n x m3dchunk_t more chunks follow
   119   *      CMAP color map chunk (optional)
   120   *      TMAP texture map chunk (optional)
   121   *      VRTS vertex data chunk (optional if it's a material library)
   122   *      BONE bind-pose skeleton, bone hierarchy chunk (optional)
   123   *          n x m3db_t contains propably more, but at least one bone
   124   *          n x m3ds_t skin group records
   125   *      MTRL* material chunk(s), can be more (optional)
   126   *          n x m3dp_t each material contains propapbly more, but at least one property
   127   *                     the properties are configurable with a static array, see m3d_propertytypes
   128   *      n x m3dchunk_t at least one, but maybe more face chunks
   129   *          PROC* procedural face, or
   130   *          MESH* triangle mesh (vertex index list) or
   131   *          VOXT, VOXD* voxel image (converted to mesh) or
   132   *          SHPE* mathematical shapes like parameterized surfaces
   133   *      LBLS* annotation label chunks, can be more (optional)
   134   *      ACTN* action chunk(s), animation-pose skeletons, can be more (optional)
   135   *          n x m3dfr_t each action contains probably more, but at least one frame
   136   *              n x m3dtr_t each frame contains probably more, but at least one transformation
   137   *      ASET* inlined asset chunk(s), can be more (optional)
   138   *  OMD3 end chunk
   139   *
   140   * Typical chunks for a game engine: 3DMO, HEAD, CMAP, TMAP, VRTS, BONE, MTRL, MESH, ACTN, OMD3
   141   * Typical chunks for distibution:   3DMO, PRVW, HEAD, CMAP, TMAP, VRTS, BONE, MTRL, MESH, ACTN, ASET, OMD3
   142   * Typical chunks for voxel image:   3DMO, HEAD, CMAP, MTRL, VOXT, VOXD, VOXD, VOXD, OMD3
   143   * Typical chunks for CAD software:  3DMO, PRVW, HEAD, CMAP, TMAP, VRTS, MTRL, SHPE, LBLS, OMD3
   144   */
   145  #ifdef _MSC_VER
   146  #pragma pack(push)
   147  #pragma pack(1)
   148  #endif
   149  
   150  typedef struct {
   151      char magic[4];
   152      uint32_t length;
   153      float scale; /* deliberately not M3D_FLOAT */
   154      uint32_t types;
   155  } _pack m3dhdr_t;
   156  
   157  typedef struct {
   158      char magic[4];
   159      uint32_t length;
   160  } _pack m3dchunk_t;
   161  
   162  #ifdef _MSC_VER
   163  #pragma pack(pop)
   164  #endif
   165  
   166  /*** in-memory model structure ***/
   167  
   168  /* textmap entry */
   169  typedef struct {
   170      M3D_FLOAT u;
   171      M3D_FLOAT v;
   172  } m3dti_t;
   173  #define m3d_textureindex_t m3dti_t
   174  
   175  /* texture */
   176  typedef struct {
   177      char *name;                 /* texture name */
   178      uint8_t *d;                 /* pixels data */
   179      uint16_t w;                 /* width */
   180      uint16_t h;                 /* height */
   181      uint8_t f;                  /* format, 1 = grayscale, 2 = grayscale+alpha, 3 = rgb, 4 = rgba */
   182  } m3dtx_t;
   183  #define m3d_texturedata_t m3dtx_t
   184  
   185  typedef struct {
   186      M3D_INDEX vertexid;
   187      M3D_FLOAT weight;
   188  } m3dw_t;
   189  #define m3d_weight_t m3dw_t
   190  
   191  /* bone entry */
   192  typedef struct {
   193      M3D_INDEX parent;           /* parent bone index */
   194      char *name;                 /* name for this bone */
   195      M3D_INDEX pos;              /* vertex index position */
   196      M3D_INDEX ori;              /* vertex index orientation (quaternion) */
   197      M3D_INDEX numweight;        /* number of controlled vertices */
   198      m3dw_t *weight;             /* weights for those vertices */
   199      M3D_FLOAT mat4[16];         /* transformation matrix */
   200  } m3db_t;
   201  #define m3d_bone_t m3db_t
   202  
   203  /* skin: bone per vertex entry */
   204  typedef struct {
   205      M3D_INDEX boneid[M3D_NUMBONE];
   206      M3D_FLOAT weight[M3D_NUMBONE];
   207  } m3ds_t;
   208  #define m3d_skin_t m3ds_t
   209  
   210  /* vertex entry */
   211  typedef struct {
   212      M3D_FLOAT x;                /* 3D coordinates and weight */
   213      M3D_FLOAT y;
   214      M3D_FLOAT z;
   215      M3D_FLOAT w;
   216      uint32_t color;             /* default vertex color */
   217      M3D_INDEX skinid;           /* skin index */
   218  #ifdef M3D_VERTEXTYPE
   219      uint8_t type;
   220  #endif
   221  } m3dv_t;
   222  #define m3d_vertex_t m3dv_t
   223  
   224  /* material property formats */
   225  enum {
   226      m3dpf_color,
   227      m3dpf_uint8,
   228      m3dpf_uint16,
   229      m3dpf_uint32,
   230      m3dpf_float,
   231      m3dpf_map
   232  };
   233  typedef struct {
   234      uint8_t format;
   235      uint8_t id;
   236  #ifdef M3D_ASCII
   237  #define M3D_PROPERTYDEF(f,i,n) { (f), (i), (char*)(n) }
   238      char *key;
   239  #else
   240  #define M3D_PROPERTYDEF(f,i,n) { (f), (i) }
   241  #endif
   242  } m3dpd_t;
   243  
   244  /* material property types */
   245  /* You shouldn't change the first 8 display and first 4 physical property. Assign the rest as you like. */
   246  enum {
   247      m3dp_Kd = 0,                /* scalar display properties */
   248      m3dp_Ka,
   249      m3dp_Ks,
   250      m3dp_Ns,
   251      m3dp_Ke,
   252      m3dp_Tf,
   253      m3dp_Km,
   254      m3dp_d,
   255      m3dp_il,
   256  
   257      m3dp_Pr = 64,               /* scalar physical properties */
   258      m3dp_Pm,
   259      m3dp_Ps,
   260      m3dp_Ni,
   261      m3dp_Nt,
   262  
   263      m3dp_map_Kd = 128,          /* textured display map properties */
   264      m3dp_map_Ka,
   265      m3dp_map_Ks,
   266      m3dp_map_Ns,
   267      m3dp_map_Ke,
   268      m3dp_map_Tf,
   269      m3dp_map_Km, /* bump map */
   270      m3dp_map_D,
   271      m3dp_map_N,  /* normal map */
   272  
   273      m3dp_map_Pr = 192,          /* textured physical map properties */
   274      m3dp_map_Pm,
   275      m3dp_map_Ps,
   276      m3dp_map_Ni,
   277      m3dp_map_Nt
   278  };
   279  enum {                          /* aliases */
   280      m3dp_bump = m3dp_map_Km,
   281      m3dp_map_il = m3dp_map_N,
   282      m3dp_refl = m3dp_map_Pm
   283  };
   284  
   285  /* material property */
   286  typedef struct {
   287      uint8_t type;               /* property type, see "m3dp_*" enumeration */
   288      union {
   289          uint32_t color;         /* if value is a color, m3dpf_color */
   290          uint32_t num;           /* if value is a number, m3dpf_uint8, m3pf_uint16, m3dpf_uint32 */
   291          float    fnum;          /* if value is a floating point number, m3dpf_float */
   292          M3D_INDEX textureid;    /* if value is a texture, m3dpf_map */
   293      } value;
   294  } m3dp_t;
   295  #define m3d_property_t m3dp_t
   296  
   297  /* material entry */
   298  typedef struct {
   299      char *name;                 /* name of the material */
   300      uint8_t numprop;            /* number of properties */
   301      m3dp_t *prop;               /* properties array */
   302  } m3dm_t;
   303  #define m3d_material_t m3dm_t
   304  
   305  /* face entry */
   306  typedef struct {
   307      M3D_INDEX materialid;       /* material index */
   308      M3D_INDEX vertex[3];        /* 3D points of the triangle in CCW order */
   309      M3D_INDEX normal[3];        /* normal vectors */
   310      M3D_INDEX texcoord[3];      /* UV coordinates */
   311  #ifdef M3D_VERTEXMAX
   312      M3D_INDEX paramid;          /* parameter index */
   313      M3D_INDEX vertmax[3];       /* maximum 3D points of the triangle in CCW order */
   314  #endif
   315  } m3df_t;
   316  #define m3d_face_t m3df_t
   317  
   318  typedef struct {
   319      uint16_t count;
   320      char *name;
   321  } m3dvi_t;
   322  #define m3d_voxelitem_t m3dvi_t
   323  #define m3d_parameter_t m3dvi_t
   324  
   325  /* voxel types (voxel palette) */
   326  typedef struct {
   327      char *name;                 /* technical name of the voxel */
   328      uint8_t rotation;           /* rotation info */
   329      uint16_t voxshape;          /* voxel shape */
   330      M3D_INDEX materialid;       /* material index */
   331      uint32_t color;             /* default voxel color */
   332      M3D_INDEX skinid;           /* skin index */
   333      uint8_t numitem;            /* number of sub-voxels */
   334      m3dvi_t *item;              /* list of sub-voxels */
   335  } m3dvt_t;
   336  #define m3d_voxeltype_t m3dvt_t
   337  
   338  /* voxel data blocks */
   339  typedef struct {
   340      char *name;                 /* name of the block */
   341      int32_t x, y, z;            /* position */
   342      uint32_t w, h, d;           /* dimension */
   343      uint8_t uncertain;          /* probability */
   344      uint8_t groupid;            /* block group id */
   345      M3D_VOXEL *data;            /* voxel data, indices to voxel type */
   346  } m3dvx_t;
   347  #define m3d_voxel_t m3dvx_t
   348  
   349  /* shape command types. must match the row in m3d_commandtypes */
   350  enum {
   351      /* special commands */
   352      m3dc_use = 0,               /* use material */
   353      m3dc_inc,                   /* include another shape */
   354      m3dc_mesh,                  /* include part of polygon mesh */
   355      /* approximations */
   356      m3dc_div,                   /* subdivision by constant resolution for both u, v */
   357      m3dc_sub,                   /* subdivision by constant, different for u and v */
   358      m3dc_len,                   /* spacial subdivision by maxlength */
   359      m3dc_dist,                  /* subdivision by maxdistance and maxangle */
   360      /* modifiers */
   361      m3dc_degu,                  /* degree for both u, v */
   362      m3dc_deg,                   /* separate degree for u and v */
   363      m3dc_rangeu,                /* range for u */
   364      m3dc_range,                 /* range for u and v */
   365      m3dc_paru,                  /* u parameters (knots) */
   366      m3dc_parv,                  /* v parameters */
   367      m3dc_trim,                  /* outer trimming curve */
   368      m3dc_hole,                  /* inner trimming curve */
   369      m3dc_scrv,                  /* spacial curve */
   370      m3dc_sp,                    /* special points */
   371      /* helper curves */
   372      m3dc_bez1,                  /* Bezier 1D */
   373      m3dc_bsp1,                  /* B-spline 1D */
   374      m3dc_bez2,                  /* bezier 2D */
   375      m3dc_bsp2,                  /* B-spline 2D */
   376      /* surfaces */
   377      m3dc_bezun,                 /* Bezier 3D with control, UV, normal */
   378      m3dc_bezu,                  /* with control and UV */
   379      m3dc_bezn,                  /* with control and normal */
   380      m3dc_bez,                   /* control points only */
   381      m3dc_nurbsun,               /* B-spline 3D */
   382      m3dc_nurbsu,
   383      m3dc_nurbsn,
   384      m3dc_nurbs,
   385      m3dc_conn,                 /* connect surfaces */
   386      /* geometrical */
   387      m3dc_line,
   388      m3dc_polygon,
   389      m3dc_circle,
   390      m3dc_cylinder,
   391      m3dc_shpere,
   392      m3dc_torus,
   393      m3dc_cone,
   394      m3dc_cube
   395  };
   396  
   397  /* shape command argument types */
   398  enum {
   399      m3dcp_mi_t = 1,             /* material index */
   400      m3dcp_hi_t,                 /* shape index */
   401      m3dcp_fi_t,                 /* face index */
   402      m3dcp_ti_t,                 /* texture map index */
   403      m3dcp_vi_t,                 /* vertex index */
   404      m3dcp_qi_t,                 /* vertex index for quaternions */
   405      m3dcp_vc_t,                 /* coordinate or radius, float scalar */
   406      m3dcp_i1_t,                 /* int8 scalar */
   407      m3dcp_i2_t,                 /* int16 scalar */
   408      m3dcp_i4_t,                 /* int32 scalar */
   409      m3dcp_va_t                  /* variadic arguments */
   410  };
   411  
   412  #define M3D_CMDMAXARG 8         /* if you increase this, add more arguments to the macro below */
   413  typedef struct {
   414  #ifdef M3D_ASCII
   415  #define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (char*)(n), (p), { (a), (b), (c), (d), (e), (f), (g), (h) } }
   416      char *key;
   417  #else
   418  #define M3D_CMDDEF(t,n,p,a,b,c,d,e,f,g,h) { (p), { (a), (b), (c), (d), (e), (f), (g), (h) } }
   419  #endif
   420      uint8_t p;
   421      uint8_t a[M3D_CMDMAXARG];
   422  } m3dcd_t;
   423  
   424  /* shape command */
   425  typedef struct {
   426      uint16_t type;              /* shape type */
   427      uint32_t *arg;              /* arguments array */
   428  } m3dc_t;
   429  #define m3d_shapecommand_t m3dc_t
   430  
   431  /* shape entry */
   432  typedef struct {
   433      char *name;                 /* name of the mathematical shape */
   434      M3D_INDEX group;            /* group this shape belongs to or -1 */
   435      uint32_t numcmd;            /* number of commands */
   436      m3dc_t *cmd;                /* commands array */
   437  } m3dh_t;
   438  #define m3d_shape_t m3dh_t
   439  
   440  /* label entry */
   441  typedef struct {
   442      char *name;                 /* name of the annotation layer or NULL */
   443      char *lang;                 /* language code or NULL */
   444      char *text;                 /* the label text */
   445      uint32_t color;             /* color */
   446      M3D_INDEX vertexid;         /* the vertex the label refers to */
   447  } m3dl_t;
   448  #define m3d_label_t m3dl_t
   449  
   450  /* frame transformations / working copy skeleton entry */
   451  typedef struct {
   452      M3D_INDEX boneid;           /* selects a node in bone hierarchy */
   453      M3D_INDEX pos;              /* vertex index new position */
   454      M3D_INDEX ori;              /* vertex index new orientation (quaternion) */
   455  } m3dtr_t;
   456  #define m3d_transform_t m3dtr_t
   457  
   458  /* animation frame entry */
   459  typedef struct {
   460      uint32_t msec;              /* frame's position on the timeline, timestamp */
   461      M3D_INDEX numtransform;     /* number of transformations in this frame */
   462      m3dtr_t *transform;         /* transformations */
   463  } m3dfr_t;
   464  #define m3d_frame_t m3dfr_t
   465  
   466  /* model action entry */
   467  typedef struct {
   468      char *name;                 /* name of the action */
   469      uint32_t durationmsec;      /* duration in millisec (1/1000 sec) */
   470      M3D_INDEX numframe;         /* number of frames in this animation */
   471      m3dfr_t *frame;             /* frames array */
   472  } m3da_t;
   473  #define m3d_action_t m3da_t
   474  
   475  /* inlined asset */
   476  typedef struct {
   477      char *name;                 /* asset name (same pointer as in texture[].name) */
   478      uint8_t *data;              /* compressed asset data */
   479      uint32_t length;            /* compressed data length */
   480  } m3di_t;
   481  #define m3d_inlinedasset_t m3di_t
   482  
   483  /*** in-memory model structure ***/
   484  #define M3D_FLG_FREERAW     (1<<0)
   485  #define M3D_FLG_FREESTR     (1<<1)
   486  #define M3D_FLG_MTLLIB      (1<<2)
   487  #define M3D_FLG_GENNORM     (1<<3)
   488  
   489  typedef struct {
   490      m3dhdr_t *raw;              /* pointer to raw data */
   491      char flags;                 /* internal flags */
   492      signed char errcode;        /* returned error code */
   493      char vc_s, vi_s, si_s, ci_s, ti_s, bi_s, nb_s, sk_s, fc_s, hi_s, fi_s, vd_s, vp_s;  /* decoded sizes for types */
   494      char *name;                 /* name of the model, like "Utah teapot" */
   495      char *license;              /* usage condition or license, like "MIT", "LGPL" or "BSD-3clause" */
   496      char *author;               /* nickname, email, homepage or github URL etc. */
   497      char *desc;                 /* comments, descriptions. May contain '\n' newline character */
   498      M3D_FLOAT scale;            /* the model's bounding cube's size in SI meters */
   499      M3D_INDEX numcmap;
   500      uint32_t *cmap;             /* color map */
   501      M3D_INDEX numtmap;
   502      m3dti_t *tmap;              /* texture map indices */
   503      M3D_INDEX numtexture;
   504      m3dtx_t *texture;           /* uncompressed textures */
   505      M3D_INDEX numbone;
   506      m3db_t *bone;               /* bone hierarchy */
   507      M3D_INDEX numvertex;
   508      m3dv_t *vertex;             /* vertex data */
   509      M3D_INDEX numskin;
   510      m3ds_t *skin;               /* skin data */
   511      M3D_INDEX nummaterial;
   512      m3dm_t *material;           /* material list */
   513  #ifdef M3D_VERTEXMAX
   514      M3D_INDEX numparam;
   515      m3dvi_t *param;             /* parameters and their values list */
   516  #endif
   517      M3D_INDEX numface;
   518      m3df_t *face;               /* model face, polygon (triangle) mesh */
   519      M3D_INDEX numvoxtype;
   520      m3dvt_t *voxtype;           /* model face, voxel types */
   521      M3D_INDEX numvoxel;
   522      m3dvx_t *voxel;             /* model face, cubes compressed into voxels */
   523      M3D_INDEX numshape;
   524      m3dh_t *shape;              /* model face, shape commands */
   525      M3D_INDEX numlabel;
   526      m3dl_t *label;              /* annotation labels */
   527      M3D_INDEX numaction;
   528      m3da_t *action;             /* action animations */
   529      M3D_INDEX numinlined;
   530      m3di_t *inlined;            /* inlined assets */
   531      M3D_INDEX numextra;
   532      m3dchunk_t **extra;         /* unknown chunks, application / engine specific data probably */
   533      m3di_t preview;             /* preview chunk */
   534  } m3d_t;
   535  
   536  /*** export parameters ***/
   537  #define M3D_EXP_INT8        0
   538  #define M3D_EXP_INT16       1
   539  #define M3D_EXP_FLOAT       2
   540  #define M3D_EXP_DOUBLE      3
   541  
   542  #define M3D_EXP_NOCMAP      (1<<0)
   543  #define M3D_EXP_NOMATERIAL  (1<<1)
   544  #define M3D_EXP_NOFACE      (1<<2)
   545  #define M3D_EXP_NONORMAL    (1<<3)
   546  #define M3D_EXP_NOTXTCRD    (1<<4)
   547  #define M3D_EXP_FLIPTXTCRD  (1<<5)
   548  #define M3D_EXP_NORECALC    (1<<6)
   549  #define M3D_EXP_IDOSUCK     (1<<7)
   550  #define M3D_EXP_NOBONE      (1<<8)
   551  #define M3D_EXP_NOACTION    (1<<9)
   552  #define M3D_EXP_INLINE      (1<<10)
   553  #define M3D_EXP_EXTRA       (1<<11)
   554  #define M3D_EXP_NOZLIB      (1<<14)
   555  #define M3D_EXP_ASCII       (1<<15)
   556  #define M3D_EXP_NOVRTMAX    (1<<16)
   557  
   558  /*** error codes ***/
   559  #define M3D_SUCCESS         0
   560  #define M3D_ERR_ALLOC       -1
   561  #define M3D_ERR_BADFILE     -2
   562  #define M3D_ERR_UNIMPL      -65
   563  #define M3D_ERR_UNKPROP     -66
   564  #define M3D_ERR_UNKMESH     -67
   565  #define M3D_ERR_UNKIMG      -68
   566  #define M3D_ERR_UNKFRAME    -69
   567  #define M3D_ERR_UNKCMD      -70
   568  #define M3D_ERR_UNKVOX      -71
   569  #define M3D_ERR_TRUNC       -72
   570  #define M3D_ERR_CMAP        -73
   571  #define M3D_ERR_TMAP        -74
   572  #define M3D_ERR_VRTS        -75
   573  #define M3D_ERR_BONE        -76
   574  #define M3D_ERR_MTRL        -77
   575  #define M3D_ERR_SHPE        -78
   576  #define M3D_ERR_VOXT        -79
   577  
   578  #define M3D_ERR_ISFATAL(x)  ((x) < 0 && (x) > -65)
   579  
   580  /* callbacks */
   581  typedef unsigned char *(*m3dread_t)(char *filename, unsigned int *size);                        /* read file contents into buffer */
   582  typedef void (*m3dfree_t)(void *buffer);                                                        /* free file contents buffer */
   583  typedef int (*m3dtxsc_t)(const char *name, const void *script, uint32_t len, m3dtx_t *output);  /* interpret texture script */
   584  typedef int (*m3dprsc_t)(const char *name, const void *script, uint32_t len, m3d_t *model);     /* interpret surface script */
   585  #endif /* ifndef M3D_APIVERSION */
   586  
   587  /*** C prototypes ***/
   588  /* import / export */
   589  m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d_t *mtllib);
   590  unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size);
   591  void m3d_free(m3d_t *model);
   592  /* generate animation pose skeleton */
   593  m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t *skeleton);
   594  m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec);
   595  
   596  /* private prototypes used by both importer and exporter */
   597  char *_m3d_safestr(char *in, int morelines);
   598  
   599  /*** C implementation ***/
   600  #ifdef M3D_IMPLEMENTATION
   601  #if !defined(M3D_NOIMPORTER) || defined(M3D_EXPORTER)
   602  /* material property definitions */
   603  static m3dpd_t m3d_propertytypes[] = {
   604      M3D_PROPERTYDEF(m3dpf_color, m3dp_Kd, "Kd"),    /* diffuse color */
   605      M3D_PROPERTYDEF(m3dpf_color, m3dp_Ka, "Ka"),    /* ambient color */
   606      M3D_PROPERTYDEF(m3dpf_color, m3dp_Ks, "Ks"),    /* specular color */
   607      M3D_PROPERTYDEF(m3dpf_float, m3dp_Ns, "Ns"),    /* specular exponent */
   608      M3D_PROPERTYDEF(m3dpf_color, m3dp_Ke, "Ke"),    /* emissive (emitting light of this color) */
   609      M3D_PROPERTYDEF(m3dpf_color, m3dp_Tf, "Tf"),    /* transmission color */
   610      M3D_PROPERTYDEF(m3dpf_float, m3dp_Km, "Km"),    /* bump strength */
   611      M3D_PROPERTYDEF(m3dpf_float, m3dp_d,  "d"),     /* dissolve (transparency) */
   612      M3D_PROPERTYDEF(m3dpf_uint8, m3dp_il, "il"),    /* illumination model (informational, ignored by PBR-shaders) */
   613  
   614      M3D_PROPERTYDEF(m3dpf_float, m3dp_Pr, "Pr"),    /* roughness */
   615      M3D_PROPERTYDEF(m3dpf_float, m3dp_Pm, "Pm"),    /* metallic, also reflection */
   616      M3D_PROPERTYDEF(m3dpf_float, m3dp_Ps, "Ps"),    /* sheen */
   617      M3D_PROPERTYDEF(m3dpf_float, m3dp_Ni, "Ni"),    /* index of refraction (optical density) */
   618      M3D_PROPERTYDEF(m3dpf_float, m3dp_Nt, "Nt"),    /* thickness of face in millimeter, for printing */
   619  
   620      /* aliases, note that "map_*" aliases are handled automatically */
   621      M3D_PROPERTYDEF(m3dpf_map, m3dp_map_Km, "bump"),
   622      M3D_PROPERTYDEF(m3dpf_map, m3dp_map_N, "map_N"),/* as normal map has no scalar version, it's counterpart is 'il' */
   623      M3D_PROPERTYDEF(m3dpf_map, m3dp_map_Pm, "refl")
   624  };
   625  /* shape command definitions. if more commands start with the same string, the longer must come first */
   626  static m3dcd_t m3d_commandtypes[] = {
   627      /* technical */
   628      M3D_CMDDEF(m3dc_use,     "use",     1, m3dcp_mi_t, 0, 0, 0, 0, 0, 0, 0),
   629      M3D_CMDDEF(m3dc_inc,     "inc",     3, m3dcp_hi_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vi_t, 0, 0, 0, 0),
   630      M3D_CMDDEF(m3dc_mesh,    "mesh",    1, m3dcp_fi_t, m3dcp_fi_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vi_t, 0, 0, 0),
   631      /* approximations */
   632      M3D_CMDDEF(m3dc_div,     "div",     1, m3dcp_vc_t, 0, 0, 0, 0, 0, 0, 0),
   633      M3D_CMDDEF(m3dc_sub,     "sub",     2, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0),
   634      M3D_CMDDEF(m3dc_len,     "len",     1, m3dcp_vc_t, 0, 0, 0, 0, 0, 0, 0),
   635      M3D_CMDDEF(m3dc_dist,    "dist",    2, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0),
   636      /* modifiers */
   637      M3D_CMDDEF(m3dc_degu,    "degu",    1, m3dcp_i1_t, 0, 0, 0, 0, 0, 0, 0),
   638      M3D_CMDDEF(m3dc_deg,     "deg",     2, m3dcp_i1_t, m3dcp_i1_t, 0, 0, 0, 0, 0, 0),
   639      M3D_CMDDEF(m3dc_rangeu,  "rangeu",  1, m3dcp_ti_t, 0, 0, 0, 0, 0, 0, 0),
   640      M3D_CMDDEF(m3dc_range,   "range",   2, m3dcp_ti_t, m3dcp_ti_t, 0, 0, 0, 0, 0, 0),
   641      M3D_CMDDEF(m3dc_paru,    "paru",    2, m3dcp_va_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0),
   642      M3D_CMDDEF(m3dc_parv,    "parv",    2, m3dcp_va_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0),
   643      M3D_CMDDEF(m3dc_trim,    "trim",    3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0),
   644      M3D_CMDDEF(m3dc_hole,    "hole",    3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0),
   645      M3D_CMDDEF(m3dc_scrv,    "scrv",    3, m3dcp_va_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0, 0, 0, 0),
   646      M3D_CMDDEF(m3dc_sp,      "sp",      2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
   647      /* helper curves */
   648      M3D_CMDDEF(m3dc_bez1,    "bez1",    2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
   649      M3D_CMDDEF(m3dc_bsp1,    "bsp1",    2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
   650      M3D_CMDDEF(m3dc_bez2,    "bez2",    2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
   651      M3D_CMDDEF(m3dc_bsp2,    "bsp2",    2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
   652      /* surfaces */
   653      M3D_CMDDEF(m3dc_bezun,   "bezun",   4, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, m3dcp_vi_t, 0, 0, 0, 0),
   654      M3D_CMDDEF(m3dc_bezu,    "bezu",    3, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, 0, 0, 0, 0, 0),
   655      M3D_CMDDEF(m3dc_bezn,    "bezn",    3, m3dcp_va_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0),
   656      M3D_CMDDEF(m3dc_bez,     "bez",     2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
   657      M3D_CMDDEF(m3dc_nurbsun, "nurbsun", 4, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, m3dcp_vi_t, 0, 0, 0, 0),
   658      M3D_CMDDEF(m3dc_nurbsu,  "nurbsu",  3, m3dcp_va_t, m3dcp_vi_t, m3dcp_ti_t, 0, 0, 0, 0, 0),
   659      M3D_CMDDEF(m3dc_nurbsn,  "nurbsn",  3, m3dcp_va_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0),
   660      M3D_CMDDEF(m3dc_nurbs,   "nurbs",   2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
   661      M3D_CMDDEF(m3dc_conn,    "conn",    6, m3dcp_i2_t, m3dcp_ti_t, m3dcp_i2_t, m3dcp_i2_t, m3dcp_ti_t, m3dcp_i2_t, 0, 0),
   662      /* geometrical */
   663      M3D_CMDDEF(m3dc_line,    "line",    2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
   664      M3D_CMDDEF(m3dc_polygon, "polygon", 2, m3dcp_va_t, m3dcp_vi_t, 0, 0, 0, 0, 0, 0),
   665      M3D_CMDDEF(m3dc_circle,  "circle",  3, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, 0, 0, 0, 0, 0),
   666      M3D_CMDDEF(m3dc_cylinder,"cylinder",6, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, 0, 0),
   667      M3D_CMDDEF(m3dc_shpere,  "shpere",  2, m3dcp_vi_t, m3dcp_vc_t, 0, 0, 0, 0, 0, 0),
   668      M3D_CMDDEF(m3dc_torus,   "torus",   4, m3dcp_vi_t, m3dcp_qi_t, m3dcp_vc_t, m3dcp_vc_t, 0, 0, 0, 0),
   669      M3D_CMDDEF(m3dc_cone,    "cone",    3, m3dcp_vi_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0),
   670      M3D_CMDDEF(m3dc_cube,    "cube",    3, m3dcp_vi_t, m3dcp_vi_t, m3dcp_vi_t, 0, 0, 0, 0, 0)
   671  };
   672  #endif
   673  
   674  #include <stdlib.h>
   675  #include <string.h>
   676  
   677  #if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H)
   678  /* PNG decompressor from
   679  
   680     stb_image - v2.23 - public domain image loader - http://nothings.org/stb_image.h
   681  */
   682  static const char *_m3dstbi__g_failure_reason;
   683  
   684  enum
   685  {
   686     STBI_default = 0,
   687  
   688     STBI_grey       = 1,
   689     STBI_grey_alpha = 2,
   690     STBI_rgb        = 3,
   691     STBI_rgb_alpha  = 4
   692  };
   693  
   694  enum
   695  {
   696     STBI__SCAN_load=0,
   697     STBI__SCAN_type,
   698     STBI__SCAN_header
   699  };
   700  
   701  typedef unsigned short _m3dstbi_us;
   702  
   703  typedef uint16_t _m3dstbi__uint16;
   704  typedef int16_t  _m3dstbi__int16;
   705  typedef uint32_t _m3dstbi__uint32;
   706  typedef int32_t  _m3dstbi__int32;
   707  
   708  typedef struct
   709  {
   710     _m3dstbi__uint32 img_x, img_y;
   711     int img_n, img_out_n;
   712  
   713     void *io_user_data;
   714  
   715     int read_from_callbacks;
   716     int buflen;
   717     unsigned char buffer_start[128];
   718  
   719     unsigned char *img_buffer, *img_buffer_end;
   720     unsigned char *img_buffer_original, *img_buffer_original_end;
   721  } _m3dstbi__context;
   722  
   723  typedef struct
   724  {
   725     int bits_per_channel;
   726     int num_channels;
   727     int channel_order;
   728  } _m3dstbi__result_info;
   729  
   730  #define STBI_ASSERT(v)
   731  #define STBI_NOTUSED(v)  (void)sizeof(v)
   732  #define STBI__BYTECAST(x)  ((unsigned char) ((x) & 255))
   733  #define STBI_MALLOC(sz)           M3D_MALLOC(sz)
   734  #define STBI_REALLOC(p,newsz)     M3D_REALLOC(p,newsz)
   735  #define STBI_FREE(p)              M3D_FREE(p)
   736  #define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)
   737  
   738  _inline static unsigned char _m3dstbi__get8(_m3dstbi__context *s)
   739  {
   740     if (s->img_buffer < s->img_buffer_end)
   741        return *s->img_buffer++;
   742     return 0;
   743  }
   744  
   745  _inline static int _m3dstbi__at_eof(_m3dstbi__context *s)
   746  {
   747     return s->img_buffer >= s->img_buffer_end;
   748  }
   749  
   750  static void _m3dstbi__skip(_m3dstbi__context *s, int n)
   751  {
   752     if (n < 0) {
   753        s->img_buffer = s->img_buffer_end;
   754        return;
   755     }
   756     s->img_buffer += n;
   757  }
   758  
   759  static int _m3dstbi__getn(_m3dstbi__context *s, unsigned char *buffer, int n)
   760  {
   761     if (s->img_buffer+n <= s->img_buffer_end) {
   762        memcpy(buffer, s->img_buffer, n);
   763        s->img_buffer += n;
   764        return 1;
   765     } else
   766        return 0;
   767  }
   768  
   769  static int _m3dstbi__get16be(_m3dstbi__context *s)
   770  {
   771     int z = _m3dstbi__get8(s);
   772     return (z << 8) + _m3dstbi__get8(s);
   773  }
   774  
   775  static _m3dstbi__uint32 _m3dstbi__get32be(_m3dstbi__context *s)
   776  {
   777     _m3dstbi__uint32 z = _m3dstbi__get16be(s);
   778     return (z << 16) + _m3dstbi__get16be(s);
   779  }
   780  
   781  #define _m3dstbi__err(x,y)  _m3dstbi__errstr(y)
   782  static int _m3dstbi__errstr(const char *str)
   783  {
   784     _m3dstbi__g_failure_reason = str;
   785     return 0;
   786  }
   787  
   788  _inline static void *_m3dstbi__malloc(size_t size)
   789  {
   790      return STBI_MALLOC(size);
   791  }
   792  
   793  static int _m3dstbi__addsizes_valid(int a, int b)
   794  {
   795     if (b < 0) return 0;
   796     return a <= 2147483647 - b;
   797  }
   798  
   799  static int _m3dstbi__mul2sizes_valid(int a, int b)
   800  {
   801     if (a < 0 || b < 0) return 0;
   802     if (b == 0) return 1;
   803     return a <= 2147483647/b;
   804  }
   805  
   806  static int _m3dstbi__mad2sizes_valid(int a, int b, int add)
   807  {
   808     return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__addsizes_valid(a*b, add);
   809  }
   810  
   811  static int _m3dstbi__mad3sizes_valid(int a, int b, int c, int add)
   812  {
   813     return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__mul2sizes_valid(a*b, c) &&
   814        _m3dstbi__addsizes_valid(a*b*c, add);
   815  }
   816  
   817  static void *_m3dstbi__malloc_mad2(int a, int b, int add)
   818  {
   819     if (!_m3dstbi__mad2sizes_valid(a, b, add)) return NULL;
   820     return _m3dstbi__malloc(a*b + add);
   821  }
   822  
   823  static void *_m3dstbi__malloc_mad3(int a, int b, int c, int add)
   824  {
   825     if (!_m3dstbi__mad3sizes_valid(a, b, c, add)) return NULL;
   826     return _m3dstbi__malloc(a*b*c + add);
   827  }
   828  
   829  static unsigned char _m3dstbi__compute_y(int r, int g, int b)
   830  {
   831     return (unsigned char) (((r*77) + (g*150) +  (29*b)) >> 8);
   832  }
   833  
   834  static unsigned char *_m3dstbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y)
   835  {
   836     int i,j;
   837     unsigned char *good;
   838  
   839     if (req_comp == img_n) return data;
   840     STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
   841  
   842     good = (unsigned char *) _m3dstbi__malloc_mad3(req_comp, x, y, 0);
   843     if (good == NULL) {
   844        STBI_FREE(data);
   845        _m3dstbi__err("outofmem", "Out of memory");
   846        return NULL;
   847     }
   848  
   849     for (j=0; j < (int) y; ++j) {
   850        unsigned char *src  = data + j * x * img_n   ;
   851        unsigned char *dest = good + j * x * req_comp;
   852  
   853        #define STBI__COMBO(a,b)  ((a)*8+(b))
   854        #define STBI__CASE(a,b)   case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
   855        switch (STBI__COMBO(img_n, req_comp)) {
   856           STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255;                                     } break;
   857           STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0];                                  } break;
   858           STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255;                     } break;
   859           STBI__CASE(2,1) { dest[0]=src[0];                                                  } break;
   860           STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0];                                  } break;
   861           STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1];                  } break;
   862           STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255;        } break;
   863           STBI__CASE(3,1) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]);                   } break;
   864           STBI__CASE(3,2) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]), dest[1] = 255;    } break;
   865           STBI__CASE(4,1) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]);                   } break;
   866           STBI__CASE(4,2) { dest[0]=_m3dstbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break;
   867           STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2];                    } break;
   868           default: STBI_ASSERT(0);
   869        }
   870        #undef STBI__CASE
   871     }
   872  
   873     STBI_FREE(data);
   874     return good;
   875  }
   876  
   877  static _m3dstbi__uint16 _m3dstbi__compute_y_16(int r, int g, int b)
   878  {
   879     return (_m3dstbi__uint16) (((r*77) + (g*150) +  (29*b)) >> 8);
   880  }
   881  
   882  static _m3dstbi__uint16 *_m3dstbi__convert_format16(_m3dstbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y)
   883  {
   884     int i,j;
   885     _m3dstbi__uint16 *good;
   886  
   887     if (req_comp == img_n) return data;
   888     STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
   889  
   890     good = (_m3dstbi__uint16 *) _m3dstbi__malloc(req_comp * x * y * 2);
   891     if (good == NULL) {
   892        STBI_FREE(data);
   893        _m3dstbi__err("outofmem", "Out of memory");
   894        return NULL;
   895     }
   896  
   897     for (j=0; j < (int) y; ++j) {
   898        _m3dstbi__uint16 *src  = data + j * x * img_n   ;
   899        _m3dstbi__uint16 *dest = good + j * x * req_comp;
   900  
   901        #define STBI__COMBO(a,b)  ((a)*8+(b))
   902        #define STBI__CASE(a,b)   case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
   903        switch (STBI__COMBO(img_n, req_comp)) {
   904           STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff;                                     } break;
   905           STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0];                                     } break;
   906           STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff;                     } break;
   907           STBI__CASE(2,1) { dest[0]=src[0];                                                     } break;
   908           STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0];                                     } break;
   909           STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1];                     } break;
   910           STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff;        } break;
   911           STBI__CASE(3,1) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]);                   } break;
   912           STBI__CASE(3,2) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break;
   913           STBI__CASE(4,1) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]);                   } break;
   914           STBI__CASE(4,2) { dest[0]=_m3dstbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break;
   915           STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2];                       } break;
   916           default: STBI_ASSERT(0);
   917        }
   918        #undef STBI__CASE
   919     }
   920  
   921     STBI_FREE(data);
   922     return good;
   923  }
   924  
   925  #define STBI__ZFAST_BITS  9
   926  #define STBI__ZFAST_MASK  ((1 << STBI__ZFAST_BITS) - 1)
   927  
   928  typedef struct
   929  {
   930     _m3dstbi__uint16 fast[1 << STBI__ZFAST_BITS];
   931     _m3dstbi__uint16 firstcode[16];
   932     int maxcode[17];
   933     _m3dstbi__uint16 firstsymbol[16];
   934     unsigned char  size[288];
   935     _m3dstbi__uint16 value[288];
   936  } _m3dstbi__zhuffman;
   937  
   938  _inline static int _m3dstbi__bitreverse16(int n)
   939  {
   940    n = ((n & 0xAAAA) >>  1) | ((n & 0x5555) << 1);
   941    n = ((n & 0xCCCC) >>  2) | ((n & 0x3333) << 2);
   942    n = ((n & 0xF0F0) >>  4) | ((n & 0x0F0F) << 4);
   943    n = ((n & 0xFF00) >>  8) | ((n & 0x00FF) << 8);
   944    return n;
   945  }
   946  
   947  _inline static int _m3dstbi__bit_reverse(int v, int bits)
   948  {
   949     STBI_ASSERT(bits <= 16);
   950     return _m3dstbi__bitreverse16(v) >> (16-bits);
   951  }
   952  
   953  static int _m3dstbi__zbuild_huffman(_m3dstbi__zhuffman *z, unsigned char *sizelist, int num)
   954  {
   955     int i,k=0;
   956     int code, next_code[16], sizes[17];
   957  
   958     memset(sizes, 0, sizeof(sizes));
   959     memset(z->fast, 0, sizeof(z->fast));
   960     for (i=0; i < num; ++i)
   961        ++sizes[sizelist[i]];
   962     sizes[0] = 0;
   963     for (i=1; i < 16; ++i)
   964        if (sizes[i] > (1 << i))
   965           return _m3dstbi__err("bad sizes", "Corrupt PNG");
   966     code = 0;
   967     for (i=1; i < 16; ++i) {
   968        next_code[i] = code;
   969        z->firstcode[i] = (_m3dstbi__uint16) code;
   970        z->firstsymbol[i] = (_m3dstbi__uint16) k;
   971        code = (code + sizes[i]);
   972        if (sizes[i])
   973           if (code-1 >= (1 << i)) return _m3dstbi__err("bad codelengths","Corrupt PNG");
   974        z->maxcode[i] = code << (16-i);
   975        code <<= 1;
   976        k += sizes[i];
   977     }
   978     z->maxcode[16] = 0x10000;
   979     for (i=0; i < num; ++i) {
   980        int s = sizelist[i];
   981        if (s) {
   982           int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
   983           _m3dstbi__uint16 fastv = (_m3dstbi__uint16) ((s << 9) | i);
   984           z->size [c] = (unsigned char     ) s;
   985           z->value[c] = (_m3dstbi__uint16) i;
   986           if (s <= STBI__ZFAST_BITS) {
   987              int j = _m3dstbi__bit_reverse(next_code[s],s);
   988              while (j < (1 << STBI__ZFAST_BITS)) {
   989                 z->fast[j] = fastv;
   990                 j += (1 << s);
   991              }
   992           }
   993           ++next_code[s];
   994        }
   995     }
   996     return 1;
   997  }
   998  
   999  typedef struct
  1000  {
  1001     unsigned char *zbuffer, *zbuffer_end;
  1002     int num_bits;
  1003     _m3dstbi__uint32 code_buffer;
  1004  
  1005     char *zout;
  1006     char *zout_start;
  1007     char *zout_end;
  1008     int   z_expandable;
  1009  
  1010     _m3dstbi__zhuffman z_length, z_distance;
  1011  } _m3dstbi__zbuf;
  1012  
  1013  _inline static unsigned char _m3dstbi__zget8(_m3dstbi__zbuf *z)
  1014  {
  1015     if (z->zbuffer >= z->zbuffer_end) return 0;
  1016     return *z->zbuffer++;
  1017  }
  1018  
  1019  static void _m3dstbi__fill_bits(_m3dstbi__zbuf *z)
  1020  {
  1021     do {
  1022        STBI_ASSERT(z->code_buffer < (1U << z->num_bits));
  1023        z->code_buffer |= (unsigned int) _m3dstbi__zget8(z) << z->num_bits;
  1024        z->num_bits += 8;
  1025     } while (z->num_bits <= 24);
  1026  }
  1027  
  1028  _inline static unsigned int _m3dstbi__zreceive(_m3dstbi__zbuf *z, int n)
  1029  {
  1030     unsigned int k;
  1031     if (z->num_bits < n) _m3dstbi__fill_bits(z);
  1032     k = z->code_buffer & ((1 << n) - 1);
  1033     z->code_buffer >>= n;
  1034     z->num_bits -= n;
  1035     return k;
  1036  }
  1037  
  1038  static int _m3dstbi__zhuffman_decode_slowpath(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z)
  1039  {
  1040     int b,s,k;
  1041     k = _m3dstbi__bit_reverse(a->code_buffer, 16);
  1042     for (s=STBI__ZFAST_BITS+1; ; ++s)
  1043        if (k < z->maxcode[s])
  1044           break;
  1045     if (s == 16) return -1;
  1046     b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
  1047     STBI_ASSERT(z->size[b] == s);
  1048     a->code_buffer >>= s;
  1049     a->num_bits -= s;
  1050     return z->value[b];
  1051  }
  1052  
  1053  _inline static int _m3dstbi__zhuffman_decode(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z)
  1054  {
  1055     int b,s;
  1056     if (a->num_bits < 16) _m3dstbi__fill_bits(a);
  1057     b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
  1058     if (b) {
  1059        s = b >> 9;
  1060        a->code_buffer >>= s;
  1061        a->num_bits -= s;
  1062        return b & 511;
  1063     }
  1064     return _m3dstbi__zhuffman_decode_slowpath(a, z);
  1065  }
  1066  
  1067  static int _m3dstbi__zexpand(_m3dstbi__zbuf *z, char *zout, int n)
  1068  {
  1069     char *q;
  1070     int cur, limit, old_limit;
  1071     z->zout = zout;
  1072     if (!z->z_expandable) return _m3dstbi__err("output buffer limit","Corrupt PNG");
  1073     cur   = (int) (z->zout     - z->zout_start);
  1074     limit = old_limit = (int) (z->zout_end - z->zout_start);
  1075     while (cur + n > limit)
  1076        limit *= 2;
  1077     q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);
  1078     STBI_NOTUSED(old_limit);
  1079     if (q == NULL) return _m3dstbi__err("outofmem", "Out of memory");
  1080     z->zout_start = q;
  1081     z->zout       = q + cur;
  1082     z->zout_end   = q + limit;
  1083     return 1;
  1084  }
  1085  
  1086  static int _m3dstbi__zlength_base[31] = {
  1087     3,4,5,6,7,8,9,10,11,13,
  1088     15,17,19,23,27,31,35,43,51,59,
  1089     67,83,99,115,131,163,195,227,258,0,0 };
  1090  
  1091  static int _m3dstbi__zlength_extra[31]=
  1092  { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
  1093  
  1094  static int _m3dstbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
  1095  257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
  1096  
  1097  static int _m3dstbi__zdist_extra[32] =
  1098  { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
  1099  
  1100  static int _m3dstbi__parse_huffman_block(_m3dstbi__zbuf *a)
  1101  {
  1102     char *zout = a->zout;
  1103     for(;;) {
  1104        int z = _m3dstbi__zhuffman_decode(a, &a->z_length);
  1105        if (z < 256) {
  1106           if (z < 0) return _m3dstbi__err("bad huffman code","Corrupt PNG");
  1107           if (zout >= a->zout_end) {
  1108              if (!_m3dstbi__zexpand(a, zout, 1)) return 0;
  1109              zout = a->zout;
  1110           }
  1111           *zout++ = (char) z;
  1112        } else {
  1113           unsigned char *p;
  1114           int len,dist;
  1115           if (z == 256) {
  1116              a->zout = zout;
  1117              return 1;
  1118           }
  1119           z -= 257;
  1120           len = _m3dstbi__zlength_base[z];
  1121           if (_m3dstbi__zlength_extra[z]) len += _m3dstbi__zreceive(a, _m3dstbi__zlength_extra[z]);
  1122           z = _m3dstbi__zhuffman_decode(a, &a->z_distance);
  1123           if (z < 0) return _m3dstbi__err("bad huffman code","Corrupt PNG");
  1124           dist = _m3dstbi__zdist_base[z];
  1125           if (_m3dstbi__zdist_extra[z]) dist += _m3dstbi__zreceive(a, _m3dstbi__zdist_extra[z]);
  1126           if (zout - a->zout_start < dist) return _m3dstbi__err("bad dist","Corrupt PNG");
  1127           if (zout + len > a->zout_end) {
  1128              if (!_m3dstbi__zexpand(a, zout, len)) return 0;
  1129              zout = a->zout;
  1130           }
  1131           p = (unsigned char *) (zout - dist);
  1132           if (dist == 1) {
  1133              unsigned char v = *p;
  1134              if (len) { do *zout++ = v; while (--len); }
  1135           } else {
  1136              if (len) { do *zout++ = *p++; while (--len); }
  1137           }
  1138        }
  1139     }
  1140  }
  1141  
  1142  static int _m3dstbi__compute_huffman_codes(_m3dstbi__zbuf *a)
  1143  {
  1144     static unsigned char length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
  1145     _m3dstbi__zhuffman z_codelength;
  1146     unsigned char lencodes[286+32+137];
  1147     unsigned char codelength_sizes[19];
  1148     int i,n;
  1149  
  1150     int hlit  = _m3dstbi__zreceive(a,5) + 257;
  1151     int hdist = _m3dstbi__zreceive(a,5) + 1;
  1152     int hclen = _m3dstbi__zreceive(a,4) + 4;
  1153     int ntot  = hlit + hdist;
  1154  
  1155     memset(codelength_sizes, 0, sizeof(codelength_sizes));
  1156     for (i=0; i < hclen; ++i) {
  1157        int s = _m3dstbi__zreceive(a,3);
  1158        codelength_sizes[length_dezigzag[i]] = (unsigned char) s;
  1159     }
  1160     if (!_m3dstbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
  1161  
  1162     n = 0;
  1163     while (n < ntot) {
  1164        int c = _m3dstbi__zhuffman_decode(a, &z_codelength);
  1165        if (c < 0 || c >= 19) return _m3dstbi__err("bad codelengths", "Corrupt PNG");
  1166        if (c < 16)
  1167           lencodes[n++] = (unsigned char) c;
  1168        else {
  1169           unsigned char fill = 0;
  1170           if (c == 16) {
  1171              c = _m3dstbi__zreceive(a,2)+3;
  1172              if (n == 0) return _m3dstbi__err("bad codelengths", "Corrupt PNG");
  1173              fill = lencodes[n-1];
  1174           } else if (c == 17)
  1175              c = _m3dstbi__zreceive(a,3)+3;
  1176           else {
  1177              STBI_ASSERT(c == 18);
  1178              c = _m3dstbi__zreceive(a,7)+11;
  1179           }
  1180           if (ntot - n < c) return _m3dstbi__err("bad codelengths", "Corrupt PNG");
  1181           memset(lencodes+n, fill, c);
  1182           n += c;
  1183        }
  1184     }
  1185     if (n != ntot) return _m3dstbi__err("bad codelengths","Corrupt PNG");
  1186     if (!_m3dstbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;
  1187     if (!_m3dstbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;
  1188     return 1;
  1189  }
  1190  
  1191  _inline static int _m3dstbi__parse_uncompressed_block(_m3dstbi__zbuf *a)
  1192  {
  1193     unsigned char header[4];
  1194     int len,nlen,k;
  1195     if (a->num_bits & 7)
  1196        _m3dstbi__zreceive(a, a->num_bits & 7);
  1197     k = 0;
  1198     while (a->num_bits > 0) {
  1199        header[k++] = (unsigned char) (a->code_buffer & 255);
  1200        a->code_buffer >>= 8;
  1201        a->num_bits -= 8;
  1202     }
  1203     STBI_ASSERT(a->num_bits == 0);
  1204     while (k < 4)
  1205        header[k++] = _m3dstbi__zget8(a);
  1206     len  = header[1] * 256 + header[0];
  1207     nlen = header[3] * 256 + header[2];
  1208     if (nlen != (len ^ 0xffff)) return _m3dstbi__err("zlib corrupt","Corrupt PNG");
  1209     if (a->zbuffer + len > a->zbuffer_end) return _m3dstbi__err("read past buffer","Corrupt PNG");
  1210     if (a->zout + len > a->zout_end)
  1211        if (!_m3dstbi__zexpand(a, a->zout, len)) return 0;
  1212     memcpy(a->zout, a->zbuffer, len);
  1213     a->zbuffer += len;
  1214     a->zout += len;
  1215     return 1;
  1216  }
  1217  
  1218  static int _m3dstbi__parse_zlib_header(_m3dstbi__zbuf *a)
  1219  {
  1220     int cmf   = _m3dstbi__zget8(a);
  1221     int cm    = cmf & 15;
  1222     /* int cinfo = cmf >> 4; */
  1223     int flg   = _m3dstbi__zget8(a);
  1224     if ((cmf*256+flg) % 31 != 0) return _m3dstbi__err("bad zlib header","Corrupt PNG");
  1225     if (flg & 32) return _m3dstbi__err("no preset dict","Corrupt PNG");
  1226     if (cm != 8) return _m3dstbi__err("bad compression","Corrupt PNG");
  1227     return 1;
  1228  }
  1229  
  1230  static unsigned char _m3dstbi__zdefault_length[288], _m3dstbi__zdefault_distance[32];
  1231  static void _m3dstbi__init_zdefaults(void)
  1232  {
  1233     int i;
  1234     for (i=0; i <= 143; ++i)     _m3dstbi__zdefault_length[i]   = 8;
  1235     for (   ; i <= 255; ++i)     _m3dstbi__zdefault_length[i]   = 9;
  1236     for (   ; i <= 279; ++i)     _m3dstbi__zdefault_length[i]   = 7;
  1237     for (   ; i <= 287; ++i)     _m3dstbi__zdefault_length[i]   = 8;
  1238  
  1239     for (i=0; i <=  31; ++i)     _m3dstbi__zdefault_distance[i] = 5;
  1240  }
  1241  
  1242  static int _m3dstbi__parse_zlib(_m3dstbi__zbuf *a, int parse_header)
  1243  {
  1244     int final, type;
  1245     if (parse_header)
  1246        if (!_m3dstbi__parse_zlib_header(a)) return 0;
  1247     a->num_bits = 0;
  1248     a->code_buffer = 0;
  1249     do {
  1250        final = _m3dstbi__zreceive(a,1);
  1251        type = _m3dstbi__zreceive(a,2);
  1252        if (type == 0) {
  1253           if (!_m3dstbi__parse_uncompressed_block(a)) return 0;
  1254        } else if (type == 3) {
  1255           return 0;
  1256        } else {
  1257           if (type == 1) {
  1258              if (!_m3dstbi__zbuild_huffman(&a->z_length  , _m3dstbi__zdefault_length  , 288)) return 0;
  1259              if (!_m3dstbi__zbuild_huffman(&a->z_distance, _m3dstbi__zdefault_distance,  32)) return 0;
  1260           } else {
  1261              if (!_m3dstbi__compute_huffman_codes(a)) return 0;
  1262           }
  1263           if (!_m3dstbi__parse_huffman_block(a)) return 0;
  1264        }
  1265     } while (!final);
  1266     return 1;
  1267  }
  1268  
  1269  static int _m3dstbi__do_zlib(_m3dstbi__zbuf *a, char *obuf, int olen, int exp, int parse_header)
  1270  {
  1271     a->zout_start = obuf;
  1272     a->zout       = obuf;
  1273     a->zout_end   = obuf + olen;
  1274     a->z_expandable = exp;
  1275     _m3dstbi__init_zdefaults();
  1276     return _m3dstbi__parse_zlib(a, parse_header);
  1277  }
  1278  
  1279  char *_m3dstbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)
  1280  {
  1281     _m3dstbi__zbuf a;
  1282     char *p = (char *) _m3dstbi__malloc(initial_size);
  1283     if (p == NULL) return NULL;
  1284     a.zbuffer = (unsigned char *) buffer;
  1285     a.zbuffer_end = (unsigned char *) buffer + len;
  1286     if (_m3dstbi__do_zlib(&a, p, initial_size, 1, parse_header)) {
  1287        if (outlen) *outlen = (int) (a.zout - a.zout_start);
  1288        return a.zout_start;
  1289     } else {
  1290        STBI_FREE(a.zout_start);
  1291        return NULL;
  1292     }
  1293  }
  1294  
  1295  typedef struct
  1296  {
  1297     _m3dstbi__uint32 length;
  1298     _m3dstbi__uint32 type;
  1299  } _m3dstbi__pngchunk;
  1300  
  1301  static _m3dstbi__pngchunk _m3dstbi__get_chunk_header(_m3dstbi__context *s)
  1302  {
  1303     _m3dstbi__pngchunk c;
  1304     c.length = _m3dstbi__get32be(s);
  1305     c.type   = _m3dstbi__get32be(s);
  1306     return c;
  1307  }
  1308  
  1309  _inline static int _m3dstbi__check_png_header(_m3dstbi__context *s)
  1310  {
  1311     static unsigned char png_sig[8] = { 137,80,78,71,13,10,26,10 };
  1312     int i;
  1313     for (i=0; i < 8; ++i)
  1314        if (_m3dstbi__get8(s) != png_sig[i]) return _m3dstbi__err("bad png sig","Not a PNG");
  1315     return 1;
  1316  }
  1317  
  1318  typedef struct
  1319  {
  1320     _m3dstbi__context *s;
  1321     unsigned char *idata, *expanded, *out;
  1322     int depth;
  1323  } _m3dstbi__png;
  1324  
  1325  
  1326  enum {
  1327     STBI__F_none=0,
  1328     STBI__F_sub=1,
  1329     STBI__F_up=2,
  1330     STBI__F_avg=3,
  1331     STBI__F_paeth=4,
  1332     STBI__F_avg_first,
  1333     STBI__F_paeth_first
  1334  };
  1335  
  1336  static unsigned char first_row_filter[5] =
  1337  {
  1338     STBI__F_none,
  1339     STBI__F_sub,
  1340     STBI__F_none,
  1341     STBI__F_avg_first,
  1342     STBI__F_paeth_first
  1343  };
  1344  
  1345  static int _m3dstbi__paeth(int a, int b, int c)
  1346  {
  1347     int p = a + b - c;
  1348     int pa = abs(p-a);
  1349     int pb = abs(p-b);
  1350     int pc = abs(p-c);
  1351     if (pa <= pb && pa <= pc) return a;
  1352     if (pb <= pc) return b;
  1353     return c;
  1354  }
  1355  
  1356  static unsigned char _m3dstbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
  1357  
  1358  static int _m3dstbi__create_png_image_raw(_m3dstbi__png *a, unsigned char *raw, _m3dstbi__uint32 raw_len, int out_n, _m3dstbi__uint32 x, _m3dstbi__uint32 y, int depth, int color)
  1359  {
  1360     int bytes = (depth == 16? 2 : 1);
  1361     _m3dstbi__context *s = a->s;
  1362     _m3dstbi__uint32 i,j,stride = x*out_n*bytes;
  1363     _m3dstbi__uint32 img_len, img_width_bytes;
  1364     int k;
  1365     int img_n = s->img_n;
  1366  
  1367     int output_bytes = out_n*bytes;
  1368     int filter_bytes = img_n*bytes;
  1369     int width = x;
  1370  
  1371     STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
  1372     a->out = (unsigned char *) _m3dstbi__malloc_mad3(x, y, output_bytes, 0);
  1373     if (!a->out) return _m3dstbi__err("outofmem", "Out of memory");
  1374  
  1375     if (!_m3dstbi__mad3sizes_valid(img_n, x, depth, 7)) return _m3dstbi__err("too large", "Corrupt PNG");
  1376     img_width_bytes = (((img_n * x * depth) + 7) >> 3);
  1377     img_len = (img_width_bytes + 1) * y;
  1378     if (s->img_x == x && s->img_y == y) {
  1379        if (raw_len != img_len) return _m3dstbi__err("not enough pixels","Corrupt PNG");
  1380     } else {
  1381        if (raw_len < img_len) return _m3dstbi__err("not enough pixels","Corrupt PNG");
  1382     }
  1383  
  1384     for (j=0; j < y; ++j) {
  1385        unsigned char *cur = a->out + stride*j;
  1386        unsigned char *prior = cur - stride;
  1387        int filter = *raw++;
  1388  
  1389        if (filter > 4)
  1390           return _m3dstbi__err("invalid filter","Corrupt PNG");
  1391  
  1392        if (depth < 8) {
  1393           STBI_ASSERT(img_width_bytes <= x);
  1394           cur += x*out_n - img_width_bytes;
  1395           filter_bytes = 1;
  1396           width = img_width_bytes;
  1397        }
  1398        prior = cur - stride;
  1399  
  1400        if (j == 0) filter = first_row_filter[filter];
  1401  
  1402        for (k=0; k < filter_bytes; ++k) {
  1403           switch (filter) {
  1404              case STBI__F_none       : cur[k] = raw[k]; break;
  1405              case STBI__F_sub        : cur[k] = raw[k]; break;
  1406              case STBI__F_up         : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
  1407              case STBI__F_avg        : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
  1408              case STBI__F_paeth      : cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(0,prior[k],0)); break;
  1409              case STBI__F_avg_first  : cur[k] = raw[k]; break;
  1410              case STBI__F_paeth_first: cur[k] = raw[k]; break;
  1411           }
  1412        }
  1413  
  1414        if (depth == 8) {
  1415           if (img_n != out_n)
  1416              cur[img_n] = 255;
  1417           raw += img_n;
  1418           cur += out_n;
  1419           prior += out_n;
  1420        } else if (depth == 16) {
  1421           if (img_n != out_n) {
  1422              cur[filter_bytes]   = 255;
  1423              cur[filter_bytes+1] = 255;
  1424           }
  1425           raw += filter_bytes;
  1426           cur += output_bytes;
  1427           prior += output_bytes;
  1428        } else {
  1429           raw += 1;
  1430           cur += 1;
  1431           prior += 1;
  1432        }
  1433  
  1434        if (depth < 8 || img_n == out_n) {
  1435           int nk = (width - 1)*filter_bytes;
  1436           #define STBI__CASE(f) \
  1437               case f:     \
  1438                  for (k=0; k < nk; ++k)
  1439           switch (filter) {
  1440              case STBI__F_none:         memcpy(cur, raw, nk); break;
  1441              STBI__CASE(STBI__F_sub)          { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
  1442              STBI__CASE(STBI__F_up)           { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
  1443              STBI__CASE(STBI__F_avg)          { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
  1444              STBI__CASE(STBI__F_paeth)        { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
  1445              STBI__CASE(STBI__F_avg_first)    { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
  1446              STBI__CASE(STBI__F_paeth_first)  { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k-filter_bytes],0,0)); } break;
  1447           }
  1448           #undef STBI__CASE
  1449           raw += nk;
  1450        } else {
  1451           STBI_ASSERT(img_n+1 == out_n);
  1452           #define STBI__CASE(f) \
  1453               case f:     \
  1454                  for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
  1455                     for (k=0; k < filter_bytes; ++k)
  1456           switch (filter) {
  1457              STBI__CASE(STBI__F_none)         { cur[k] = raw[k]; } break;
  1458              STBI__CASE(STBI__F_sub)          { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
  1459              STBI__CASE(STBI__F_up)           { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
  1460              STBI__CASE(STBI__F_avg)          { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
  1461              STBI__CASE(STBI__F_paeth)        { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
  1462              STBI__CASE(STBI__F_avg_first)    { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
  1463              STBI__CASE(STBI__F_paeth_first)  { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k- output_bytes],0,0)); } break;
  1464           }
  1465           #undef STBI__CASE
  1466  
  1467           if (depth == 16) {
  1468              cur = a->out + stride*j;
  1469              for (i=0; i < x; ++i,cur+=output_bytes) {
  1470                 cur[filter_bytes+1] = 255;
  1471              }
  1472           }
  1473        }
  1474     }
  1475  
  1476     if (depth < 8) {
  1477        for (j=0; j < y; ++j) {
  1478           unsigned char *cur = a->out + stride*j;
  1479           unsigned char *in  = a->out + stride*j + x*out_n - img_width_bytes;
  1480           unsigned char scale = (color == 0) ? _m3dstbi__depth_scale_table[depth] : 1;
  1481  
  1482           if (depth == 4) {
  1483              for (k=x*img_n; k >= 2; k-=2, ++in) {
  1484                 *cur++ = scale * ((*in >> 4)       );
  1485                 *cur++ = scale * ((*in     ) & 0x0f);
  1486              }
  1487              if (k > 0) *cur++ = scale * ((*in >> 4)       );
  1488           } else if (depth == 2) {
  1489              for (k=x*img_n; k >= 4; k-=4, ++in) {
  1490                 *cur++ = scale * ((*in >> 6)       );
  1491                 *cur++ = scale * ((*in >> 4) & 0x03);
  1492                 *cur++ = scale * ((*in >> 2) & 0x03);
  1493                 *cur++ = scale * ((*in     ) & 0x03);
  1494              }
  1495              if (k > 0) *cur++ = scale * ((*in >> 6)       );
  1496              if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
  1497              if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
  1498           } else if (depth == 1) {
  1499              for (k=x*img_n; k >= 8; k-=8, ++in) {
  1500                 *cur++ = scale * ((*in >> 7)       );
  1501                 *cur++ = scale * ((*in >> 6) & 0x01);
  1502                 *cur++ = scale * ((*in >> 5) & 0x01);
  1503                 *cur++ = scale * ((*in >> 4) & 0x01);
  1504                 *cur++ = scale * ((*in >> 3) & 0x01);
  1505                 *cur++ = scale * ((*in >> 2) & 0x01);
  1506                 *cur++ = scale * ((*in >> 1) & 0x01);
  1507                 *cur++ = scale * ((*in     ) & 0x01);
  1508              }
  1509              if (k > 0) *cur++ = scale * ((*in >> 7)       );
  1510              if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
  1511              if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
  1512              if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
  1513              if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
  1514              if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
  1515              if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
  1516           }
  1517           if (img_n != out_n) {
  1518              int q;
  1519              cur = a->out + stride*j;
  1520              if (img_n == 1) {
  1521                 for (q=x-1; q >= 0; --q) {
  1522                    cur[q*2+1] = 255;
  1523                    cur[q*2+0] = cur[q];
  1524                 }
  1525              } else {
  1526                 STBI_ASSERT(img_n == 3);
  1527                 for (q=x-1; q >= 0; --q) {
  1528                    cur[q*4+3] = 255;
  1529                    cur[q*4+2] = cur[q*3+2];
  1530                    cur[q*4+1] = cur[q*3+1];
  1531                    cur[q*4+0] = cur[q*3+0];
  1532                 }
  1533              }
  1534           }
  1535        }
  1536     } else if (depth == 16) {
  1537        unsigned char *cur = a->out;
  1538        _m3dstbi__uint16 *cur16 = (_m3dstbi__uint16*)cur;
  1539  
  1540        for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
  1541           *cur16 = (cur[0] << 8) | cur[1];
  1542        }
  1543     }
  1544  
  1545     return 1;
  1546  }
  1547  
  1548  static int _m3dstbi__create_png_image(_m3dstbi__png *a, unsigned char *image_data, _m3dstbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)
  1549  {
  1550     int bytes = (depth == 16 ? 2 : 1);
  1551     int out_bytes = out_n * bytes;
  1552     unsigned char *final;
  1553     int p;
  1554     if (!interlaced)
  1555        return _m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);
  1556  
  1557     final = (unsigned char *) _m3dstbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
  1558     for (p=0; p < 7; ++p) {
  1559        int xorig[] = { 0,4,0,2,0,1,0 };
  1560        int yorig[] = { 0,0,4,0,2,0,1 };
  1561        int xspc[]  = { 8,8,4,4,2,2,1 };
  1562        int yspc[]  = { 8,8,8,4,4,2,2 };
  1563        int i,j,x,y;
  1564        x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
  1565        y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
  1566        if (x && y) {
  1567           _m3dstbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
  1568           if (!_m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
  1569              STBI_FREE(final);
  1570              return 0;
  1571           }
  1572           for (j=0; j < y; ++j) {
  1573              for (i=0; i < x; ++i) {
  1574                 int out_y = j*yspc[p]+yorig[p];
  1575                 int out_x = i*xspc[p]+xorig[p];
  1576                 memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes,
  1577                        a->out + (j*x+i)*out_bytes, out_bytes);
  1578              }
  1579           }
  1580           STBI_FREE(a->out);
  1581           image_data += img_len;
  1582           image_data_len -= img_len;
  1583        }
  1584     }
  1585     a->out = final;
  1586  
  1587     return 1;
  1588  }
  1589  
  1590  static int _m3dstbi__compute_transparency(_m3dstbi__png *z, unsigned char tc[3], int out_n)
  1591  {
  1592     _m3dstbi__context *s = z->s;
  1593     _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y;
  1594     unsigned char *p = z->out;
  1595  
  1596     STBI_ASSERT(out_n == 2 || out_n == 4);
  1597  
  1598     if (out_n == 2) {
  1599        for (i=0; i < pixel_count; ++i) {
  1600           p[1] = (p[0] == tc[0] ? 0 : 255);
  1601           p += 2;
  1602        }
  1603     } else {
  1604        for (i=0; i < pixel_count; ++i) {
  1605           if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
  1606              p[3] = 0;
  1607           p += 4;
  1608        }
  1609     }
  1610     return 1;
  1611  }
  1612  
  1613  static int _m3dstbi__compute_transparency16(_m3dstbi__png *z, _m3dstbi__uint16 tc[3], int out_n)
  1614  {
  1615     _m3dstbi__context *s = z->s;
  1616     _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y;
  1617     _m3dstbi__uint16 *p = (_m3dstbi__uint16*) z->out;
  1618  
  1619     STBI_ASSERT(out_n == 2 || out_n == 4);
  1620  
  1621     if (out_n == 2) {
  1622        for (i = 0; i < pixel_count; ++i) {
  1623           p[1] = (p[0] == tc[0] ? 0 : 65535);
  1624           p += 2;
  1625        }
  1626     } else {
  1627        for (i = 0; i < pixel_count; ++i) {
  1628           if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
  1629              p[3] = 0;
  1630           p += 4;
  1631        }
  1632     }
  1633     return 1;
  1634  }
  1635  
  1636  static int _m3dstbi__expand_png_palette(_m3dstbi__png *a, unsigned char *palette, int len, int pal_img_n)
  1637  {
  1638     _m3dstbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
  1639     unsigned char *p, *temp_out, *orig = a->out;
  1640  
  1641     p = (unsigned char *) _m3dstbi__malloc_mad2(pixel_count, pal_img_n, 0);
  1642     if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory");
  1643  
  1644     temp_out = p;
  1645  
  1646     if (pal_img_n == 3) {
  1647        for (i=0; i < pixel_count; ++i) {
  1648           int n = orig[i]*4;
  1649           p[0] = palette[n  ];
  1650           p[1] = palette[n+1];
  1651           p[2] = palette[n+2];
  1652           p += 3;
  1653        }
  1654     } else {
  1655        for (i=0; i < pixel_count; ++i) {
  1656           int n = orig[i]*4;
  1657           p[0] = palette[n  ];
  1658           p[1] = palette[n+1];
  1659           p[2] = palette[n+2];
  1660           p[3] = palette[n+3];
  1661           p += 4;
  1662        }
  1663     }
  1664     STBI_FREE(a->out);
  1665     a->out = temp_out;
  1666  
  1667     STBI_NOTUSED(len);
  1668  
  1669     return 1;
  1670  }
  1671  
  1672  #define STBI__PNG_TYPE(a,b,c,d)  (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d))
  1673  
  1674  static int _m3dstbi__parse_png_file(_m3dstbi__png *z, int scan, int req_comp)
  1675  {
  1676     unsigned char palette[1024], pal_img_n=0;
  1677     unsigned char has_trans=0, tc[3];
  1678     _m3dstbi__uint16 tc16[3];
  1679     _m3dstbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
  1680     int first=1,k,interlace=0, color=0;
  1681     _m3dstbi__context *s = z->s;
  1682  
  1683     z->expanded = NULL;
  1684     z->idata = NULL;
  1685     z->out = NULL;
  1686  
  1687     if (!_m3dstbi__check_png_header(s)) return 0;
  1688  
  1689     if (scan == STBI__SCAN_type) return 1;
  1690  
  1691     for (;;) {
  1692        _m3dstbi__pngchunk c = _m3dstbi__get_chunk_header(s);
  1693        switch (c.type) {
  1694           case STBI__PNG_TYPE('C','g','B','I'):
  1695              _m3dstbi__skip(s, c.length);
  1696              break;
  1697           case STBI__PNG_TYPE('I','H','D','R'): {
  1698              int comp,filter;
  1699              if (!first) return _m3dstbi__err("multiple IHDR","Corrupt PNG");
  1700              first = 0;
  1701              if (c.length != 13) return _m3dstbi__err("bad IHDR len","Corrupt PNG");
  1702              s->img_x = _m3dstbi__get32be(s); if (s->img_x > (1 << 24)) return _m3dstbi__err("too large","Very large image (corrupt?)");
  1703              s->img_y = _m3dstbi__get32be(s); if (s->img_y > (1 << 24)) return _m3dstbi__err("too large","Very large image (corrupt?)");
  1704              z->depth = _m3dstbi__get8(s);  if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16)  return _m3dstbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only");
  1705              color = _m3dstbi__get8(s);  if (color > 6)         return _m3dstbi__err("bad ctype","Corrupt PNG");
  1706              if (color == 3 && z->depth == 16)                  return _m3dstbi__err("bad ctype","Corrupt PNG");
  1707              if (color == 3) pal_img_n = 3; else if (color & 1) return _m3dstbi__err("bad ctype","Corrupt PNG");
  1708              comp  = _m3dstbi__get8(s);  if (comp) return _m3dstbi__err("bad comp method","Corrupt PNG");
  1709              filter= _m3dstbi__get8(s);  if (filter) return _m3dstbi__err("bad filter method","Corrupt PNG");
  1710              interlace = _m3dstbi__get8(s); if (interlace>1) return _m3dstbi__err("bad interlace method","Corrupt PNG");
  1711              if (!s->img_x || !s->img_y) return _m3dstbi__err("0-pixel image","Corrupt PNG");
  1712              if (!pal_img_n) {
  1713                 s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
  1714                 if ((1 << 30) / s->img_x / s->img_n < s->img_y) return _m3dstbi__err("too large", "Image too large to decode");
  1715                 if (scan == STBI__SCAN_header) return 1;
  1716              } else {
  1717                 s->img_n = 1;
  1718                 if ((1 << 30) / s->img_x / 4 < s->img_y) return _m3dstbi__err("too large","Corrupt PNG");
  1719              }
  1720              break;
  1721           }
  1722  
  1723           case STBI__PNG_TYPE('P','L','T','E'):  {
  1724              if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG");
  1725              if (c.length > 256*3) return _m3dstbi__err("invalid PLTE","Corrupt PNG");
  1726              pal_len = c.length / 3;
  1727              if (pal_len * 3 != c.length) return _m3dstbi__err("invalid PLTE","Corrupt PNG");
  1728              for (i=0; i < pal_len; ++i) {
  1729                 palette[i*4+0] = _m3dstbi__get8(s);
  1730                 palette[i*4+1] = _m3dstbi__get8(s);
  1731                 palette[i*4+2] = _m3dstbi__get8(s);
  1732                 palette[i*4+3] = 255;
  1733              }
  1734              break;
  1735           }
  1736  
  1737           case STBI__PNG_TYPE('t','R','N','S'): {
  1738              if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG");
  1739              if (z->idata) return _m3dstbi__err("tRNS after IDAT","Corrupt PNG");
  1740              if (pal_img_n) {
  1741                 if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; }
  1742                 if (pal_len == 0) return _m3dstbi__err("tRNS before PLTE","Corrupt PNG");
  1743                 if (c.length > pal_len) return _m3dstbi__err("bad tRNS len","Corrupt PNG");
  1744                 pal_img_n = 4;
  1745                 for (i=0; i < c.length; ++i)
  1746                    palette[i*4+3] = _m3dstbi__get8(s);
  1747              } else {
  1748                 if (!(s->img_n & 1)) return _m3dstbi__err("tRNS with alpha","Corrupt PNG");
  1749                 if (c.length != (_m3dstbi__uint32) s->img_n*2) return _m3dstbi__err("bad tRNS len","Corrupt PNG");
  1750                 has_trans = 1;
  1751                 if (z->depth == 16) {
  1752                    for (k = 0; k < s->img_n; ++k) tc16[k] = (_m3dstbi__uint16)_m3dstbi__get16be(s);
  1753                 } else {
  1754                    for (k = 0; k < s->img_n; ++k) tc[k] = (unsigned char)(_m3dstbi__get16be(s) & 255) * _m3dstbi__depth_scale_table[z->depth];
  1755                 }
  1756              }
  1757              break;
  1758           }
  1759  
  1760           case STBI__PNG_TYPE('I','D','A','T'): {
  1761              if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG");
  1762              if (pal_img_n && !pal_len) return _m3dstbi__err("no PLTE","Corrupt PNG");
  1763              if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
  1764              if ((int)(ioff + c.length) < (int)ioff) return 0;
  1765              if (ioff + c.length > idata_limit) {
  1766                 _m3dstbi__uint32 idata_limit_old = idata_limit;
  1767                 unsigned char *p;
  1768                 if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
  1769                 while (ioff + c.length > idata_limit)
  1770                    idata_limit *= 2;
  1771                 STBI_NOTUSED(idata_limit_old);
  1772                 p = (unsigned char *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory");
  1773                 z->idata = p;
  1774              }
  1775              if (!_m3dstbi__getn(s, z->idata+ioff,c.length)) return _m3dstbi__err("outofdata","Corrupt PNG");
  1776              ioff += c.length;
  1777              break;
  1778           }
  1779  
  1780           case STBI__PNG_TYPE('I','E','N','D'): {
  1781              _m3dstbi__uint32 raw_len, bpl;
  1782              if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG");
  1783              if (scan != STBI__SCAN_load) return 1;
  1784              if (z->idata == NULL) return _m3dstbi__err("no IDAT","Corrupt PNG");
  1785              bpl = (s->img_x * z->depth + 7) / 8;
  1786              raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
  1787              z->expanded = (unsigned char *) _m3dstbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, 1);
  1788              if (z->expanded == NULL) return 0;
  1789              STBI_FREE(z->idata); z->idata = NULL;
  1790              if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
  1791                 s->img_out_n = s->img_n+1;
  1792              else
  1793                 s->img_out_n = s->img_n;
  1794              if (!_m3dstbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;
  1795              if (has_trans) {
  1796                 if (z->depth == 16) {
  1797                    if (!_m3dstbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;
  1798                 } else {
  1799                    if (!_m3dstbi__compute_transparency(z, tc, s->img_out_n)) return 0;
  1800                 }
  1801              }
  1802              if (pal_img_n) {
  1803                 s->img_n = pal_img_n;
  1804                 s->img_out_n = pal_img_n;
  1805                 if (req_comp >= 3) s->img_out_n = req_comp;
  1806                 if (!_m3dstbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
  1807                    return 0;
  1808              } else if (has_trans) {
  1809                 ++s->img_n;
  1810              }
  1811              STBI_FREE(z->expanded); z->expanded = NULL;
  1812              return 1;
  1813           }
  1814  
  1815           default:
  1816              if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG");
  1817              if ((c.type & (1 << 29)) == 0) {
  1818                 return _m3dstbi__err("invalid_chunk", "PNG not supported: unknown PNG chunk type");
  1819              }
  1820              _m3dstbi__skip(s, c.length);
  1821              break;
  1822        }
  1823        _m3dstbi__get32be(s);
  1824     }
  1825  }
  1826  
  1827  static void *_m3dstbi__do_png(_m3dstbi__png *p, int *x, int *y, int *n, int req_comp, _m3dstbi__result_info *ri)
  1828  {
  1829     void *result=NULL;
  1830     if (req_comp < 0 || req_comp > 4) { _m3dstbi__err("bad req_comp", "Internal error"); return NULL; }
  1831     if (_m3dstbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
  1832        if (p->depth < 8)
  1833           ri->bits_per_channel = 8;
  1834        else
  1835           ri->bits_per_channel = p->depth;
  1836        result = p->out;
  1837        p->out = NULL;
  1838        if (req_comp && req_comp != p->s->img_out_n) {
  1839           if (ri->bits_per_channel == 8)
  1840              result = _m3dstbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
  1841           else
  1842              result = _m3dstbi__convert_format16((_m3dstbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
  1843           p->s->img_out_n = req_comp;
  1844           if (result == NULL) return result;
  1845        }
  1846        *x = p->s->img_x;
  1847        *y = p->s->img_y;
  1848        if (n) *n = p->s->img_n;
  1849     }
  1850     STBI_FREE(p->out);      p->out      = NULL;
  1851     STBI_FREE(p->expanded); p->expanded = NULL;
  1852     STBI_FREE(p->idata);    p->idata    = NULL;
  1853  
  1854     return result;
  1855  }
  1856  
  1857  static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp, int req_comp, _m3dstbi__result_info *ri)
  1858  {
  1859     _m3dstbi__png p;
  1860     p.s = s;
  1861     return _m3dstbi__do_png(&p, x,y,comp,req_comp, ri);
  1862  }
  1863  #define stbi__context _m3dstbi__context
  1864  #define stbi__result_info _m3dstbi__result_info
  1865  #define stbi__png_load _m3dstbi__png_load
  1866  #define stbi_zlib_decode_malloc_guesssize_headerflag _m3dstbi_zlib_decode_malloc_guesssize_headerflag
  1867  #endif
  1868  #if !defined(M3D_NOIMPORTER) && defined(STBI_INCLUDE_STB_IMAGE_H) && !defined(STB_IMAGE_IMPLEMENTATION)
  1869  #error "stb_image.h included without STB_IMAGE_IMPLEMENTATION. Sorry, we need some stuff defined inside the ifguard for proper integration"
  1870  #endif
  1871  
  1872  #if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H)
  1873  /* zlib_compressor from
  1874  
  1875     stb_image_write - v1.13 - public domain - http://nothings.org/stb/stb_image_write.h
  1876  */
  1877  typedef unsigned char _m3dstbiw__uc;
  1878  typedef unsigned short _m3dstbiw__us;
  1879  
  1880  typedef uint16_t _m3dstbiw__uint16;
  1881  typedef int16_t  _m3dstbiw__int16;
  1882  typedef uint32_t _m3dstbiw__uint32;
  1883  typedef int32_t  _m3dstbiw__int32;
  1884  
  1885  #define STBIW_MALLOC(s)     M3D_MALLOC(s)
  1886  #define STBIW_REALLOC(p,ns) M3D_REALLOC(p,ns)
  1887  #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz)
  1888  #define STBIW_FREE          M3D_FREE
  1889  #define STBIW_MEMMOVE       memmove
  1890  #define STBIW_UCHAR         (uint8_t)
  1891  #define STBIW_ASSERT(x)
  1892  #define _m3dstbiw___sbraw(a)     ((int *) (a) - 2)
  1893  #define _m3dstbiw___sbm(a)       _m3dstbiw___sbraw(a)[0]
  1894  #define _m3dstbiw___sbn(a)       _m3dstbiw___sbraw(a)[1]
  1895  
  1896  #define _m3dstbiw___sbneedgrow(a,n)  ((a)==0 || _m3dstbiw___sbn(a)+n >= _m3dstbiw___sbm(a))
  1897  #define _m3dstbiw___sbmaybegrow(a,n) (_m3dstbiw___sbneedgrow(a,(n)) ? _m3dstbiw___sbgrow(a,n) : 0)
  1898  #define _m3dstbiw___sbgrow(a,n)  _m3dstbiw___sbgrowf((void **) &(a), (n), sizeof(*(a)))
  1899  
  1900  #define _m3dstbiw___sbpush(a, v)      (_m3dstbiw___sbmaybegrow(a,1), (a)[_m3dstbiw___sbn(a)++] = (v))
  1901  #define _m3dstbiw___sbcount(a)        ((a) ? _m3dstbiw___sbn(a) : 0)
  1902  #define _m3dstbiw___sbfree(a)         ((a) ? STBIW_FREE(_m3dstbiw___sbraw(a)),0 : 0)
  1903  
  1904  static void *_m3dstbiw___sbgrowf(void **arr, int increment, int itemsize)
  1905  {
  1906     int m = *arr ? 2*_m3dstbiw___sbm(*arr)+increment : increment+1;
  1907     void *p = STBIW_REALLOC_SIZED(*arr ? _m3dstbiw___sbraw(*arr) : 0, *arr ? (_m3dstbiw___sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);
  1908     STBIW_ASSERT(p);
  1909     if (p) {
  1910        if (!*arr) ((int *) p)[1] = 0;
  1911        *arr = (void *) ((int *) p + 2);
  1912        _m3dstbiw___sbm(*arr) = m;
  1913     }
  1914     return *arr;
  1915  }
  1916  
  1917  static unsigned char *_m3dstbiw___zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
  1918  {
  1919     while (*bitcount >= 8) {
  1920        _m3dstbiw___sbpush(data, STBIW_UCHAR(*bitbuffer));
  1921        *bitbuffer >>= 8;
  1922        *bitcount -= 8;
  1923     }
  1924     return data;
  1925  }
  1926  
  1927  static int _m3dstbiw___zlib_bitrev(int code, int codebits)
  1928  {
  1929     int res=0;
  1930     while (codebits--) {
  1931        res = (res << 1) | (code & 1);
  1932        code >>= 1;
  1933     }
  1934     return res;
  1935  }
  1936  
  1937  static unsigned int _m3dstbiw___zlib_countm(unsigned char *a, unsigned char *b, int limit)
  1938  {
  1939     int i;
  1940     for (i=0; i < limit && i < 258; ++i)
  1941        if (a[i] != b[i]) break;
  1942     return i;
  1943  }
  1944  
  1945  static unsigned int _m3dstbiw___zhash(unsigned char *data)
  1946  {
  1947     _m3dstbiw__uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
  1948     hash ^= hash << 3;
  1949     hash += hash >> 5;
  1950     hash ^= hash << 4;
  1951     hash += hash >> 17;
  1952     hash ^= hash << 25;
  1953     hash += hash >> 6;
  1954     return hash;
  1955  }
  1956  
  1957  #define _m3dstbiw___zlib_flush() (out = _m3dstbiw___zlib_flushf(out, &bitbuf, &bitcount))
  1958  #define _m3dstbiw___zlib_add(code,codebits) \
  1959        (bitbuf |= (code) << bitcount, bitcount += (codebits), _m3dstbiw___zlib_flush())
  1960  #define _m3dstbiw___zlib_huffa(b,c)  _m3dstbiw___zlib_add(_m3dstbiw___zlib_bitrev(b,c),c)
  1961  #define _m3dstbiw___zlib_huff1(n)  _m3dstbiw___zlib_huffa(0x30 + (n), 8)
  1962  #define _m3dstbiw___zlib_huff2(n)  _m3dstbiw___zlib_huffa(0x190 + (n)-144, 9)
  1963  #define _m3dstbiw___zlib_huff3(n)  _m3dstbiw___zlib_huffa(0 + (n)-256,7)
  1964  #define _m3dstbiw___zlib_huff4(n)  _m3dstbiw___zlib_huffa(0xc0 + (n)-280,8)
  1965  #define _m3dstbiw___zlib_huff(n)  ((n) <= 143 ? _m3dstbiw___zlib_huff1(n) : (n) <= 255 ? _m3dstbiw___zlib_huff2(n) : (n) <= 279 ? _m3dstbiw___zlib_huff3(n) : _m3dstbiw___zlib_huff4(n))
  1966  #define _m3dstbiw___zlib_huffb(n) ((n) <= 143 ? _m3dstbiw___zlib_huff1(n) : _m3dstbiw___zlib_huff2(n))
  1967  
  1968  #define _m3dstbiw___ZHASH   16384
  1969  
  1970  unsigned char * _m3dstbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
  1971  {
  1972     static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };
  1973     static unsigned char  lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,  4,  5,  5,  5,  5,  0 };
  1974     static unsigned short distc[]   = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };
  1975     static unsigned char  disteb[]  = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
  1976     unsigned int bitbuf=0;
  1977     int i,j, bitcount=0;
  1978     unsigned char *out = NULL;
  1979     unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(_m3dstbiw___ZHASH * sizeof(char**));
  1980     if (hash_table == NULL)
  1981        return NULL;
  1982     if (quality < 5) quality = 5;
  1983  
  1984     _m3dstbiw___sbpush(out, 0x78);
  1985     _m3dstbiw___sbpush(out, 0x5e);
  1986     _m3dstbiw___zlib_add(1,1);
  1987     _m3dstbiw___zlib_add(1,2);
  1988  
  1989     for (i=0; i < _m3dstbiw___ZHASH; ++i)
  1990        hash_table[i] = NULL;
  1991  
  1992     i=0;
  1993     while (i < data_len-3) {
  1994        int h = _m3dstbiw___zhash(data+i)&(_m3dstbiw___ZHASH-1), best=3;
  1995        unsigned char *bestloc = 0;
  1996        unsigned char **hlist = hash_table[h];
  1997        int n = _m3dstbiw___sbcount(hlist);
  1998        for (j=0; j < n; ++j) {
  1999           if (hlist[j]-data > i-32768) {
  2000              int d = _m3dstbiw___zlib_countm(hlist[j], data+i, data_len-i);
  2001              if (d >= best) best=d,bestloc=hlist[j];
  2002           }
  2003        }
  2004        if (hash_table[h] && _m3dstbiw___sbn(hash_table[h]) == 2*quality) {
  2005           STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
  2006           _m3dstbiw___sbn(hash_table[h]) = quality;
  2007        }
  2008        _m3dstbiw___sbpush(hash_table[h],data+i);
  2009  
  2010        if (bestloc) {
  2011           h = _m3dstbiw___zhash(data+i+1)&(_m3dstbiw___ZHASH-1);
  2012           hlist = hash_table[h];
  2013           n = _m3dstbiw___sbcount(hlist);
  2014           for (j=0; j < n; ++j) {
  2015              if (hlist[j]-data > i-32767) {
  2016                 int e = _m3dstbiw___zlib_countm(hlist[j], data+i+1, data_len-i-1);
  2017                 if (e > best) {
  2018                    bestloc = NULL;
  2019                    break;
  2020                 }
  2021              }
  2022           }
  2023        }
  2024  
  2025        if (bestloc) {
  2026           int d = (int) (data+i - bestloc);
  2027           STBIW_ASSERT(d <= 32767 && best <= 258);
  2028           for (j=0; best > lengthc[j+1]-1; ++j);
  2029           _m3dstbiw___zlib_huff(j+257);
  2030           if (lengtheb[j]) _m3dstbiw___zlib_add(best - lengthc[j], lengtheb[j]);
  2031           for (j=0; d > distc[j+1]-1; ++j);
  2032           _m3dstbiw___zlib_add(_m3dstbiw___zlib_bitrev(j,5),5);
  2033           if (disteb[j]) _m3dstbiw___zlib_add(d - distc[j], disteb[j]);
  2034           i += best;
  2035        } else {
  2036           _m3dstbiw___zlib_huffb(data[i]);
  2037           ++i;
  2038        }
  2039     }
  2040     for (;i < data_len; ++i)
  2041        _m3dstbiw___zlib_huffb(data[i]);
  2042     _m3dstbiw___zlib_huff(256);
  2043     while (bitcount)
  2044        _m3dstbiw___zlib_add(0,1);
  2045  
  2046     for (i=0; i < _m3dstbiw___ZHASH; ++i)
  2047        (void) _m3dstbiw___sbfree(hash_table[i]);
  2048     STBIW_FREE(hash_table);
  2049  
  2050     {
  2051        unsigned int s1=1, s2=0;
  2052        int blocklen = (int) (data_len % 5552);
  2053        j=0;
  2054        while (j < data_len) {
  2055           for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1;
  2056           s1 %= 65521, s2 %= 65521;
  2057           j += blocklen;
  2058           blocklen = 5552;
  2059        }
  2060        _m3dstbiw___sbpush(out, STBIW_UCHAR(s2 >> 8));
  2061        _m3dstbiw___sbpush(out, STBIW_UCHAR(s2));
  2062        _m3dstbiw___sbpush(out, STBIW_UCHAR(s1 >> 8));
  2063        _m3dstbiw___sbpush(out, STBIW_UCHAR(s1));
  2064     }
  2065     *out_len = _m3dstbiw___sbn(out);
  2066     STBIW_MEMMOVE(_m3dstbiw___sbraw(out), out, *out_len);
  2067     return (unsigned char *) _m3dstbiw___sbraw(out);
  2068  }
  2069  #define stbi_zlib_compress _m3dstbi_zlib_compress
  2070  #else
  2071  unsigned char * _m3dstbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality);
  2072  #endif
  2073  
  2074  #define M3D_CHUNKMAGIC(m, a,b,c,d) ((m)[0]==(a) && (m)[1]==(b) && (m)[2]==(c) && (m)[3]==(d))
  2075  
  2076  #ifdef M3D_ASCII
  2077  #include <stdio.h>          /* get sprintf */
  2078  #include <locale.h>         /* sprintf and strtod cares about number locale */
  2079  #endif
  2080  #ifdef M3D_PROFILING
  2081  #include <sys/time.h>
  2082  #endif
  2083  
  2084  #if !defined(M3D_NOIMPORTER) && defined(M3D_ASCII)
  2085  /* helper functions for the ASCII parser */
  2086  static char *_m3d_findarg(char *s) {
  2087      while(s && *s && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n') s++;
  2088      while(s && *s && (*s == ' ' || *s == '\t')) s++;
  2089      return s;
  2090  }
  2091  static char *_m3d_findnl(char *s) {
  2092      while(s && *s && *s != '\r' && *s != '\n') s++;
  2093      if(*s == '\r') s++;
  2094      if(*s == '\n') s++;
  2095      return s;
  2096  }
  2097  static char *_m3d_gethex(char *s, uint32_t *ret)
  2098  {
  2099      if(*s == '#') s++;
  2100      *ret = 0;
  2101      for(; *s; s++) {
  2102          if(*s >= '0' && *s <= '9') {      *ret <<= 4; *ret += (uint32_t)(*s-'0'); }
  2103          else if(*s >= 'a' && *s <= 'f') { *ret <<= 4; *ret += (uint32_t)(*s-'a'+10); }
  2104          else if(*s >= 'A' && *s <= 'F') { *ret <<= 4; *ret += (uint32_t)(*s-'A'+10); }
  2105          else break;
  2106      }
  2107      return _m3d_findarg(s);
  2108  }
  2109  static char *_m3d_getint(char *s, uint32_t *ret)
  2110  {
  2111      char *e = s;
  2112      if(!s || !*s || *s == '\r' || *s == '\n') return s;
  2113      for(; *e >= '0' && *e <= '9'; e++);
  2114      *ret = atoi(s);
  2115      return e;
  2116  }
  2117  static char *_m3d_getfloat(char *s, M3D_FLOAT *ret)
  2118  {
  2119      char *e = s;
  2120      if(!s || !*s || *s == '\r' || *s == '\n') return s;
  2121      for(; *e == '-' || *e == '+' || *e == '.' || (*e >= '0' && *e <= '9') || *e == 'e' || *e == 'E'; e++);
  2122      *ret = (M3D_FLOAT)strtod(s, NULL);
  2123      return _m3d_findarg(e);
  2124  }
  2125  #endif
  2126  #if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_ASCII) || defined(M3D_EXPORTER))
  2127  /* helper function to create safe strings */
  2128  char *_m3d_safestr(char *in, int morelines)
  2129  {
  2130      char *out, *o, *i = in;
  2131      int l;
  2132      if(!in || !*in) {
  2133          out = (char*)M3D_MALLOC(1);
  2134          if(!out) return NULL;
  2135          out[0] =0;
  2136      } else {
  2137          for(o = in, l = 0; *o && ((morelines & 1) || (*o != '\r' && *o != '\n')) && l < 256; o++, l++);
  2138          out = o = (char*)M3D_MALLOC(l+1);
  2139          if(!out) return NULL;
  2140          while(*i == ' ' || *i == '\t' || *i == '\r' || (morelines && *i == '\n')) i++;
  2141          for(; *i && (morelines || (*i != '\r' && *i != '\n')); i++) {
  2142              if(*i == '\r') continue;
  2143              if(*i == '\n') {
  2144                  if(morelines >= 3 && o > out && *(o-1) == '\n') break;
  2145                  if(i > in && *(i-1) == '\n') continue;
  2146                  if(morelines & 1) {
  2147                      if(morelines == 1) *o++ = '\r';
  2148                      *o++ = '\n';
  2149                  } else
  2150                      break;
  2151              } else
  2152              if(*i == ' ' || *i == '\t') {
  2153                  *o++ = morelines? ' ' : '_';
  2154              } else
  2155                  *o++ = !morelines && (*i == '/' || *i == '\\') ? '_' : *i;
  2156          }
  2157          for(; o > out && (*(o-1) == ' ' || *(o-1) == '\t' || *(o-1) == '\r' || *(o-1) == '\n'); o--);
  2158          *o = 0;
  2159          out = (char*)M3D_REALLOC(out, (uintptr_t)o - (uintptr_t)out + 1);
  2160      }
  2161      return out;
  2162  }
  2163  #endif
  2164  #ifndef M3D_NOIMPORTER
  2165  /* helper function to load and decode/generate a texture */
  2166  M3D_INDEX _m3d_gettx(m3d_t *model, m3dread_t readfilecb, m3dfree_t freecb, char *fn)
  2167  {
  2168      unsigned int i, len = 0;
  2169      unsigned char *buff = NULL;
  2170      char *fn2;
  2171      unsigned int w, h;
  2172      stbi__context s;
  2173      stbi__result_info ri;
  2174  
  2175      /* do we have loaded this texture already? */
  2176      for(i = 0; i < model->numtexture; i++)
  2177          if(!strcmp(fn, model->texture[i].name)) return i;
  2178      /* see if it's inlined in the model */
  2179      if(model->inlined) {
  2180          for(i = 0; i < model->numinlined; i++)
  2181              if(!strcmp(fn, model->inlined[i].name)) {
  2182                  buff = model->inlined[i].data;
  2183                  len = model->inlined[i].length;
  2184                  freecb = NULL;
  2185                  break;
  2186              }
  2187      }
  2188      /* try to load from external source */
  2189      if(!buff && readfilecb) {
  2190          i = (unsigned int)strlen(fn);
  2191          if(i < 5 || fn[i - 4] != '.') {
  2192              fn2 = (char*)M3D_MALLOC(i + 5);
  2193              if(!fn2) { model->errcode = M3D_ERR_ALLOC; return M3D_UNDEF; }
  2194              memcpy(fn2, fn, i);
  2195              memcpy(fn2+i, ".png", 5);
  2196              buff = (*readfilecb)(fn2, &len);
  2197              M3D_FREE(fn2);
  2198          }
  2199          if(!buff) {
  2200              buff = (*readfilecb)(fn, &len);
  2201              if(!buff) return M3D_UNDEF;
  2202          }
  2203      }
  2204      /* add to textures array */
  2205      i = model->numtexture++;
  2206      model->texture = (m3dtx_t*)M3D_REALLOC(model->texture, model->numtexture * sizeof(m3dtx_t));
  2207      if(!model->texture) {
  2208          if(buff && freecb) (*freecb)(buff);
  2209          model->errcode = M3D_ERR_ALLOC;
  2210          return M3D_UNDEF;
  2211      }
  2212      model->texture[i].name = fn;
  2213      model->texture[i].w = model->texture[i].h = 0; model->texture[i].d = NULL;
  2214      if(buff) {
  2215          if(buff[0] == 0x89 && buff[1] == 'P' && buff[2] == 'N' && buff[3] == 'G') {
  2216              s.read_from_callbacks = 0;
  2217              s.img_buffer = s.img_buffer_original = (unsigned char *) buff;
  2218              s.img_buffer_end = s.img_buffer_original_end = (unsigned char *) buff+len;
  2219              /* don't use model->texture[i].w directly, it's a uint16_t */
  2220              w = h = len = 0;
  2221              ri.bits_per_channel = 8;
  2222              model->texture[i].d = (uint8_t*)stbi__png_load(&s, (int*)&w, (int*)&h, (int*)&len, 0, &ri);
  2223              model->texture[i].w = w;
  2224              model->texture[i].h = h;
  2225              model->texture[i].f = (uint8_t)len;
  2226          } else {
  2227  #ifdef M3D_TX_INTERP
  2228              if((model->errcode = M3D_TX_INTERP(fn, buff, len, &model->texture[i])) != M3D_SUCCESS) {
  2229                  M3D_LOG("Unable to generate texture");
  2230                  M3D_LOG(fn);
  2231              }
  2232  #else
  2233              M3D_LOG("Unimplemented interpreter");
  2234              M3D_LOG(fn);
  2235  #endif
  2236          }
  2237          if(freecb) (*freecb)(buff);
  2238      }
  2239      if(!model->texture[i].d)
  2240          model->errcode = M3D_ERR_UNKIMG;
  2241      return i;
  2242  }
  2243  
  2244  /* helper function to load and generate a procedural surface */
  2245  void _m3d_getpr(m3d_t *model, _unused m3dread_t readfilecb, _unused  m3dfree_t freecb, _unused char *fn)
  2246  {
  2247  #ifdef M3D_PR_INTERP
  2248      unsigned int i, len = 0;
  2249      unsigned char *buff = readfilecb ? (*readfilecb)(fn, &len) : NULL;
  2250  
  2251      if(!buff && model->inlined) {
  2252          for(i = 0; i < model->numinlined; i++)
  2253              if(!strcmp(fn, model->inlined[i].name)) {
  2254                  buff = model->inlined[i].data;
  2255                  len = model->inlined[i].length;
  2256                  freecb = NULL;
  2257                  break;
  2258              }
  2259      }
  2260      if(!buff || !len || (model->errcode = M3D_PR_INTERP(fn, buff, len, model)) != M3D_SUCCESS) {
  2261          M3D_LOG("Unable to generate procedural surface");
  2262          M3D_LOG(fn);
  2263          model->errcode = M3D_ERR_UNKIMG;
  2264      }
  2265      if(freecb && buff) (*freecb)(buff);
  2266  #else
  2267      (void)readfilecb;
  2268      (void)freecb;
  2269      (void)fn;
  2270      M3D_LOG("Unimplemented interpreter");
  2271      M3D_LOG(fn);
  2272      model->errcode = M3D_ERR_UNIMPL;
  2273  #endif
  2274  }
  2275  /* helpers to read indices from data stream */
  2276  #define M3D_GETSTR(x) do{offs=0;data=_m3d_getidx(data,model->si_s,&offs);x=offs?((char*)model->raw+16+offs):NULL;}while(0)
  2277  _inline static unsigned char *_m3d_getidx(unsigned char *data, char type, M3D_INDEX *idx)
  2278  {
  2279      switch(type) {
  2280          case 1: *idx = data[0] > 253 ? (int8_t)data[0] : data[0]; data++; break;
  2281          case 2: *idx = *((uint16_t*)data) > 65533 ? *((int16_t*)data) : *((uint16_t*)data); data += 2; break;
  2282          case 4: *idx = *((int32_t*)data); data += 4; break;
  2283      }
  2284      return data;
  2285  }
  2286  
  2287  #ifndef M3D_NOANIMATION
  2288  /* multiply 4 x 4 matrices. Do not use float *r[16] as argument, because some compilers misinterpret that as
  2289   * 16 pointers each pointing to a float, but we need a single pointer to 16 floats. */
  2290  void _m3d_mul(M3D_FLOAT *r, M3D_FLOAT *a, M3D_FLOAT *b)
  2291  {
  2292      r[ 0] = b[ 0] * a[ 0] + b[ 4] * a[ 1] + b[ 8] * a[ 2] + b[12] * a[ 3];
  2293      r[ 1] = b[ 1] * a[ 0] + b[ 5] * a[ 1] + b[ 9] * a[ 2] + b[13] * a[ 3];
  2294      r[ 2] = b[ 2] * a[ 0] + b[ 6] * a[ 1] + b[10] * a[ 2] + b[14] * a[ 3];
  2295      r[ 3] = b[ 3] * a[ 0] + b[ 7] * a[ 1] + b[11] * a[ 2] + b[15] * a[ 3];
  2296      r[ 4] = b[ 0] * a[ 4] + b[ 4] * a[ 5] + b[ 8] * a[ 6] + b[12] * a[ 7];
  2297      r[ 5] = b[ 1] * a[ 4] + b[ 5] * a[ 5] + b[ 9] * a[ 6] + b[13] * a[ 7];
  2298      r[ 6] = b[ 2] * a[ 4] + b[ 6] * a[ 5] + b[10] * a[ 6] + b[14] * a[ 7];
  2299      r[ 7] = b[ 3] * a[ 4] + b[ 7] * a[ 5] + b[11] * a[ 6] + b[15] * a[ 7];
  2300      r[ 8] = b[ 0] * a[ 8] + b[ 4] * a[ 9] + b[ 8] * a[10] + b[12] * a[11];
  2301      r[ 9] = b[ 1] * a[ 8] + b[ 5] * a[ 9] + b[ 9] * a[10] + b[13] * a[11];
  2302      r[10] = b[ 2] * a[ 8] + b[ 6] * a[ 9] + b[10] * a[10] + b[14] * a[11];
  2303      r[11] = b[ 3] * a[ 8] + b[ 7] * a[ 9] + b[11] * a[10] + b[15] * a[11];
  2304      r[12] = b[ 0] * a[12] + b[ 4] * a[13] + b[ 8] * a[14] + b[12] * a[15];
  2305      r[13] = b[ 1] * a[12] + b[ 5] * a[13] + b[ 9] * a[14] + b[13] * a[15];
  2306      r[14] = b[ 2] * a[12] + b[ 6] * a[13] + b[10] * a[14] + b[14] * a[15];
  2307      r[15] = b[ 3] * a[12] + b[ 7] * a[13] + b[11] * a[14] + b[15] * a[15];
  2308  }
  2309  /* calculate 4 x 4 matrix inverse */
  2310  void _m3d_inv(M3D_FLOAT *m)
  2311  {
  2312      M3D_FLOAT r[16];
  2313      M3D_FLOAT det =
  2314            m[ 0]*m[ 5]*m[10]*m[15] - m[ 0]*m[ 5]*m[11]*m[14] + m[ 0]*m[ 6]*m[11]*m[13] - m[ 0]*m[ 6]*m[ 9]*m[15]
  2315          + m[ 0]*m[ 7]*m[ 9]*m[14] - m[ 0]*m[ 7]*m[10]*m[13] - m[ 1]*m[ 6]*m[11]*m[12] + m[ 1]*m[ 6]*m[ 8]*m[15]
  2316          - m[ 1]*m[ 7]*m[ 8]*m[14] + m[ 1]*m[ 7]*m[10]*m[12] - m[ 1]*m[ 4]*m[10]*m[15] + m[ 1]*m[ 4]*m[11]*m[14]
  2317          + m[ 2]*m[ 7]*m[ 8]*m[13] - m[ 2]*m[ 7]*m[ 9]*m[12] + m[ 2]*m[ 4]*m[ 9]*m[15] - m[ 2]*m[ 4]*m[11]*m[13]
  2318          + m[ 2]*m[ 5]*m[11]*m[12] - m[ 2]*m[ 5]*m[ 8]*m[15] - m[ 3]*m[ 4]*m[ 9]*m[14] + m[ 3]*m[ 4]*m[10]*m[13]
  2319          - m[ 3]*m[ 5]*m[10]*m[12] + m[ 3]*m[ 5]*m[ 8]*m[14] - m[ 3]*m[ 6]*m[ 8]*m[13] + m[ 3]*m[ 6]*m[ 9]*m[12];
  2320      if(det == (M3D_FLOAT)0.0 || det == (M3D_FLOAT)-0.0) det = (M3D_FLOAT)1.0; else det = (M3D_FLOAT)1.0 / det;
  2321      r[ 0] = det *(m[ 5]*(m[10]*m[15] - m[11]*m[14]) + m[ 6]*(m[11]*m[13] - m[ 9]*m[15]) + m[ 7]*(m[ 9]*m[14] - m[10]*m[13]));
  2322      r[ 1] = -det*(m[ 1]*(m[10]*m[15] - m[11]*m[14]) + m[ 2]*(m[11]*m[13] - m[ 9]*m[15]) + m[ 3]*(m[ 9]*m[14] - m[10]*m[13]));
  2323      r[ 2] = det *(m[ 1]*(m[ 6]*m[15] - m[ 7]*m[14]) + m[ 2]*(m[ 7]*m[13] - m[ 5]*m[15]) + m[ 3]*(m[ 5]*m[14] - m[ 6]*m[13]));
  2324      r[ 3] = -det*(m[ 1]*(m[ 6]*m[11] - m[ 7]*m[10]) + m[ 2]*(m[ 7]*m[ 9] - m[ 5]*m[11]) + m[ 3]*(m[ 5]*m[10] - m[ 6]*m[ 9]));
  2325      r[ 4] = -det*(m[ 4]*(m[10]*m[15] - m[11]*m[14]) + m[ 6]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 7]*(m[ 8]*m[14] - m[10]*m[12]));
  2326      r[ 5] = det *(m[ 0]*(m[10]*m[15] - m[11]*m[14]) + m[ 2]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 3]*(m[ 8]*m[14] - m[10]*m[12]));
  2327      r[ 6] = -det*(m[ 0]*(m[ 6]*m[15] - m[ 7]*m[14]) + m[ 2]*(m[ 7]*m[12] - m[ 4]*m[15]) + m[ 3]*(m[ 4]*m[14] - m[ 6]*m[12]));
  2328      r[ 7] = det *(m[ 0]*(m[ 6]*m[11] - m[ 7]*m[10]) + m[ 2]*(m[ 7]*m[ 8] - m[ 4]*m[11]) + m[ 3]*(m[ 4]*m[10] - m[ 6]*m[ 8]));
  2329      r[ 8] = det *(m[ 4]*(m[ 9]*m[15] - m[11]*m[13]) + m[ 5]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 7]*(m[ 8]*m[13] - m[ 9]*m[12]));
  2330      r[ 9] = -det*(m[ 0]*(m[ 9]*m[15] - m[11]*m[13]) + m[ 1]*(m[11]*m[12] - m[ 8]*m[15]) + m[ 3]*(m[ 8]*m[13] - m[ 9]*m[12]));
  2331      r[10] = det *(m[ 0]*(m[ 5]*m[15] - m[ 7]*m[13]) + m[ 1]*(m[ 7]*m[12] - m[ 4]*m[15]) + m[ 3]*(m[ 4]*m[13] - m[ 5]*m[12]));
  2332      r[11] = -det*(m[ 0]*(m[ 5]*m[11] - m[ 7]*m[ 9]) + m[ 1]*(m[ 7]*m[ 8] - m[ 4]*m[11]) + m[ 3]*(m[ 4]*m[ 9] - m[ 5]*m[ 8]));
  2333      r[12] = -det*(m[ 4]*(m[ 9]*m[14] - m[10]*m[13]) + m[ 5]*(m[10]*m[12] - m[ 8]*m[14]) + m[ 6]*(m[ 8]*m[13] - m[ 9]*m[12]));
  2334      r[13] = det *(m[ 0]*(m[ 9]*m[14] - m[10]*m[13]) + m[ 1]*(m[10]*m[12] - m[ 8]*m[14]) + m[ 2]*(m[ 8]*m[13] - m[ 9]*m[12]));
  2335      r[14] = -det*(m[ 0]*(m[ 5]*m[14] - m[ 6]*m[13]) + m[ 1]*(m[ 6]*m[12] - m[ 4]*m[14]) + m[ 2]*(m[ 4]*m[13] - m[ 5]*m[12]));
  2336      r[15] = det *(m[ 0]*(m[ 5]*m[10] - m[ 6]*m[ 9]) + m[ 1]*(m[ 6]*m[ 8] - m[ 4]*m[10]) + m[ 2]*(m[ 4]*m[ 9] - m[ 5]*m[ 8]));
  2337      memcpy(m, &r, sizeof(r));
  2338  }
  2339  /* compose a coloumn major 4 x 4 matrix from vec3 position and vec4 orientation/rotation quaternion */
  2340  void _m3d_mat(M3D_FLOAT *r, m3dv_t *p, m3dv_t *q)
  2341  {
  2342      if(q->x == (M3D_FLOAT)0.0 && q->y == (M3D_FLOAT)0.0 && q->z >=(M3D_FLOAT) 0.7071065 && q->z <= (M3D_FLOAT)0.7071075 &&
  2343          q->w == (M3D_FLOAT)0.0) {
  2344          r[ 1] = r[ 2] = r[ 4] = r[ 6] = r[ 8] = r[ 9] = (M3D_FLOAT)0.0;
  2345          r[ 0] = r[ 5] = r[10] = (M3D_FLOAT)-1.0;
  2346      } else {
  2347          r[ 0] = 1 - 2 * (q->y * q->y + q->z * q->z); if(r[ 0]>-M3D_EPSILON && r[ 0]<M3D_EPSILON) r[ 0]=(M3D_FLOAT)0.0;
  2348          r[ 1] = 2 * (q->x * q->y - q->z * q->w);     if(r[ 1]>-M3D_EPSILON && r[ 1]<M3D_EPSILON) r[ 1]=(M3D_FLOAT)0.0;
  2349          r[ 2] = 2 * (q->x * q->z + q->y * q->w);     if(r[ 2]>-M3D_EPSILON && r[ 2]<M3D_EPSILON) r[ 2]=(M3D_FLOAT)0.0;
  2350          r[ 4] = 2 * (q->x * q->y + q->z * q->w);     if(r[ 4]>-M3D_EPSILON && r[ 4]<M3D_EPSILON) r[ 4]=(M3D_FLOAT)0.0;
  2351          r[ 5] = 1 - 2 * (q->x * q->x + q->z * q->z); if(r[ 5]>-M3D_EPSILON && r[ 5]<M3D_EPSILON) r[ 5]=(M3D_FLOAT)0.0;
  2352          r[ 6] = 2 * (q->y * q->z - q->x * q->w);     if(r[ 6]>-M3D_EPSILON && r[ 6]<M3D_EPSILON) r[ 6]=(M3D_FLOAT)0.0;
  2353          r[ 8] = 2 * (q->x * q->z - q->y * q->w);     if(r[ 8]>-M3D_EPSILON && r[ 8]<M3D_EPSILON) r[ 8]=(M3D_FLOAT)0.0;
  2354          r[ 9] = 2 * (q->y * q->z + q->x * q->w);     if(r[ 9]>-M3D_EPSILON && r[ 9]<M3D_EPSILON) r[ 9]=(M3D_FLOAT)0.0;
  2355          r[10] = 1 - 2 * (q->x * q->x + q->y * q->y); if(r[10]>-M3D_EPSILON && r[10]<M3D_EPSILON) r[10]=(M3D_FLOAT)0.0;
  2356      }
  2357      r[ 3] = p->x; r[ 7] = p->y; r[11] = p->z;
  2358      r[12] = 0; r[13] = 0; r[14] = 0; r[15] = 1;
  2359  }
  2360  #endif
  2361  #if !defined(M3D_NOANIMATION) || !defined(M3D_NONORMALS)
  2362  /* portable fast inverse square root calculation. returns 1/sqrt(x) */
  2363  static M3D_FLOAT _m3d_rsq(M3D_FLOAT x)
  2364  {
  2365  #ifdef M3D_DOUBLE
  2366      return ((M3D_FLOAT)15.0/(M3D_FLOAT)8.0) + ((M3D_FLOAT)-5.0/(M3D_FLOAT)4.0)*x + ((M3D_FLOAT)3.0/(M3D_FLOAT)8.0)*x*x;
  2367  #else
  2368      /* John Carmack's */
  2369      float x2 = x * 0.5f;
  2370      uint32_t *i = (uint32_t*)&x;
  2371      *i = (0x5f3759df - (*i >> 1));
  2372      return x * (1.5f - (x2 * x * x));
  2373  #endif
  2374  }
  2375  #endif
  2376  
  2377  /**
  2378   * Function to decode a Model 3D into in-memory format
  2379   */
  2380  m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d_t *mtllib)
  2381  {
  2382      unsigned char *end, *chunk, *buff, weights[8];
  2383      unsigned int i, j, k, l, n, am, len = 0, reclen, offs;
  2384  #ifndef M3D_NOVOXELS
  2385      int32_t min_x, min_y, min_z, max_x, max_y, max_z, sx, sy, sz, x, y, z;
  2386      M3D_INDEX edge[8], enorm;
  2387  #endif
  2388      char *name, *lang;
  2389      float f;
  2390      m3d_t *model;
  2391      M3D_INDEX mi;
  2392  #ifdef M3D_VERTEXMAX
  2393      M3D_INDEX pi;
  2394  #endif
  2395      M3D_FLOAT w;
  2396      m3dcd_t *cd;
  2397      m3dtx_t *tx;
  2398      m3dh_t *h;
  2399      m3dm_t *m;
  2400      m3da_t *a;
  2401      m3di_t *t;
  2402  #ifndef M3D_NONORMALS
  2403      char neednorm = 0;
  2404      m3dv_t *norm = NULL, *v0, *v1, *v2, va, vb;
  2405  #endif
  2406  #ifndef M3D_NOANIMATION
  2407      M3D_FLOAT r[16];
  2408  #endif
  2409  #if !defined(M3D_NOWEIGHTS) || !defined(M3D_NOANIMATION)
  2410      m3db_t *b;
  2411  #endif
  2412  #ifndef M3D_NOWEIGHTS
  2413      m3ds_t *sk;
  2414  #endif
  2415  #ifdef M3D_ASCII
  2416      m3ds_t s;
  2417      M3D_INDEX bi[M3D_BONEMAXLEVEL+1], level;
  2418      const char *ol;
  2419      char *ptr, *pe, *fn;
  2420  #endif
  2421  #ifdef M3D_PROFILING
  2422      struct timeval tv0, tv1, tvd;
  2423      gettimeofday(&tv0, NULL);
  2424  #endif
  2425  
  2426      if(!data || (!M3D_CHUNKMAGIC(data, '3','D','M','O')
  2427  #ifdef M3D_ASCII
  2428          && !M3D_CHUNKMAGIC(data, '3','d','m','o')
  2429  #endif
  2430          )) return NULL;
  2431      model = (m3d_t*)M3D_MALLOC(sizeof(m3d_t));
  2432      if(!model) {
  2433          M3D_LOG("Out of memory");
  2434          return NULL;
  2435      }
  2436      memset(model, 0, sizeof(m3d_t));
  2437  
  2438      if(mtllib) {
  2439          model->nummaterial = mtllib->nummaterial;
  2440          model->material = mtllib->material;
  2441          model->numtexture = mtllib->numtexture;
  2442          model->texture = mtllib->texture;
  2443          model->flags |= M3D_FLG_MTLLIB;
  2444      }
  2445  #ifdef M3D_ASCII
  2446      /* ASCII variant? */
  2447      if(M3D_CHUNKMAGIC(data, '3','d','m','o')) {
  2448          model->errcode = M3D_ERR_BADFILE;
  2449          model->flags |= M3D_FLG_FREESTR;
  2450          model->raw = (m3dhdr_t*)data;
  2451          ptr = (char*)data;
  2452          ol = setlocale(LC_NUMERIC, NULL);
  2453          setlocale(LC_NUMERIC, "C");
  2454          /* parse header. Don't use sscanf, that's incredibly slow */
  2455          ptr = _m3d_findarg(ptr);
  2456          if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2457          pe = _m3d_findnl(ptr);
  2458          model->scale = (float)strtod(ptr, NULL); ptr = pe;
  2459          if(model->scale <= (M3D_FLOAT)0.0) model->scale = (M3D_FLOAT)1.0;
  2460          model->name = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr);
  2461          if(!*ptr) goto asciiend;
  2462          model->license = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr);
  2463          if(!*ptr) goto asciiend;
  2464          model->author = _m3d_safestr(ptr, 2); ptr = _m3d_findnl(ptr);
  2465          if(!*ptr) goto asciiend;
  2466          if(*ptr != '\r' && *ptr != '\n')
  2467              model->desc = _m3d_safestr(ptr, 3);
  2468          while(*ptr) {
  2469              while(*ptr && *ptr!='\n') ptr++;
  2470              ptr++; if(*ptr=='\r') ptr++;
  2471              if(*ptr == '\n') break;
  2472          }
  2473  
  2474          /* the main chunk reader loop */
  2475          while(*ptr) {
  2476              while(*ptr && (*ptr == '\r' || *ptr == '\n')) ptr++;
  2477              if(!*ptr || (ptr[0]=='E' && ptr[1]=='n' && ptr[2]=='d')) break;
  2478              /* make sure there's at least one data row */
  2479              pe = ptr; ptr = _m3d_findnl(ptr);
  2480              if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2481              /* Preview chunk */
  2482              if(!memcmp(pe, "Preview", 7)) {
  2483                  if(readfilecb) {
  2484                      pe = _m3d_safestr(ptr, 0);
  2485                      if(!pe || !*pe) goto asciiend;
  2486                      model->preview.data = (*readfilecb)(pe, &model->preview.length);
  2487                      M3D_FREE(pe);
  2488                  }
  2489                  while(*ptr && *ptr != '\r' && *ptr != '\n')
  2490                      ptr = _m3d_findnl(ptr);
  2491              } else
  2492              /* texture map chunk */
  2493              if(!memcmp(pe, "Textmap", 7)) {
  2494                  if(model->tmap) { M3D_LOG("More texture map chunks, should be unique"); goto asciiend; }
  2495                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  2496                      i = model->numtmap++;
  2497                      model->tmap = (m3dti_t*)M3D_REALLOC(model->tmap, model->numtmap * sizeof(m3dti_t));
  2498                      if(!model->tmap) goto memerr;
  2499                      ptr = _m3d_getfloat(ptr, &model->tmap[i].u);
  2500                      if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2501                      _m3d_getfloat(ptr, &model->tmap[i].v);
  2502                      ptr = _m3d_findnl(ptr);
  2503                  }
  2504              } else
  2505              /* vertex chunk */
  2506              if(!memcmp(pe, "Vertex", 6)) {
  2507                  if(model->vertex) { M3D_LOG("More vertex chunks, should be unique"); goto asciiend; }
  2508                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  2509                      i = model->numvertex++;
  2510                      model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
  2511                      if(!model->vertex) goto memerr;
  2512                      memset(&model->vertex[i], 0, sizeof(m3dv_t));
  2513                      model->vertex[i].skinid = M3D_UNDEF;
  2514                      model->vertex[i].color = 0;
  2515                      model->vertex[i].w = (M3D_FLOAT)1.0;
  2516                      ptr = _m3d_getfloat(ptr, &model->vertex[i].x);
  2517                      if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2518                      ptr = _m3d_getfloat(ptr, &model->vertex[i].y);
  2519                      if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2520                      ptr = _m3d_getfloat(ptr, &model->vertex[i].z);
  2521                      if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2522                      ptr = _m3d_getfloat(ptr, &model->vertex[i].w);
  2523                      if(!*ptr) goto asciiend;
  2524                      if(*ptr == '#') {
  2525                          ptr = _m3d_gethex(ptr, &model->vertex[i].color);
  2526                          if(!*ptr) goto asciiend;
  2527                      }
  2528                      /* parse skin */
  2529                      memset(&s, 0, sizeof(m3ds_t));
  2530                      for(j = 0, w = (M3D_FLOAT)0.0; j < M3D_NUMBONE && *ptr && *ptr != '\r' && *ptr != '\n'; j++) {
  2531                          ptr = _m3d_findarg(ptr);
  2532                          if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2533                          ptr = _m3d_getint(ptr, &k);
  2534                          s.boneid[j] = (M3D_INDEX)k;
  2535                          if(*ptr == ':') {
  2536                              ptr++;
  2537                              ptr = _m3d_getfloat(ptr, &s.weight[j]);
  2538                              w += s.weight[j];
  2539                          } else if(!j)
  2540                              s.weight[j] = (M3D_FLOAT)1.0;
  2541                          if(!*ptr) goto asciiend;
  2542                      }
  2543                      if(s.boneid[0] != M3D_UNDEF && s.weight[0] > (M3D_FLOAT)0.0) {
  2544                          if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0)
  2545                              for(j = 0; j < M3D_NUMBONE && s.weight[j] > (M3D_FLOAT)0.0; j++)
  2546                                  s.weight[j] /= w;
  2547                          k = M3D_NOTDEFINED;
  2548                          if(model->skin) {
  2549                              for(j = 0; j < model->numskin; j++)
  2550                                  if(!memcmp(&model->skin[j], &s, sizeof(m3ds_t))) { k = j; break; }
  2551                          }
  2552                          if(k == M3D_NOTDEFINED) {
  2553                              k = model->numskin++;
  2554                              model->skin = (m3ds_t*)M3D_REALLOC(model->skin, model->numskin * sizeof(m3ds_t));
  2555                              if(!model->skin) goto memerr;
  2556                              memcpy(&model->skin[k], &s, sizeof(m3ds_t));
  2557                          }
  2558                          model->vertex[i].skinid = (M3D_INDEX)k;
  2559                      }
  2560                      ptr = _m3d_findnl(ptr);
  2561                  }
  2562              } else
  2563              /* Skeleton, bone hierarchy */
  2564              if(!memcmp(pe, "Bones", 5)) {
  2565                  if(model->bone) { M3D_LOG("More bones chunks, should be unique"); goto asciiend; }
  2566                  bi[0] = M3D_UNDEF;
  2567                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  2568                      i = model->numbone++;
  2569                      model->bone = (m3db_t*)M3D_REALLOC(model->bone, model->numbone * sizeof(m3db_t));
  2570                      if(!model->bone) goto memerr;
  2571                      for(level = 0; *ptr == '/'; ptr++, level++);
  2572                      if(level > M3D_BONEMAXLEVEL || !*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2573                      bi[level+1] = i;
  2574                      model->bone[i].numweight = 0;
  2575                      model->bone[i].weight = NULL;
  2576                      model->bone[i].parent = bi[level];
  2577                      ptr = _m3d_getint(ptr, &k);
  2578                      ptr = _m3d_findarg(ptr);
  2579                      if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2580                      model->bone[i].pos = (M3D_INDEX)k;
  2581                      ptr = _m3d_getint(ptr, &k);
  2582                      ptr = _m3d_findarg(ptr);
  2583                      if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2584                      model->bone[i].ori = (M3D_INDEX)k;
  2585                      model->vertex[k].skinid = M3D_INDEXMAX;
  2586                      pe = _m3d_safestr(ptr, 0);
  2587                      if(!pe || !*pe) goto asciiend;
  2588                      model->bone[i].name = pe;
  2589                      ptr = _m3d_findnl(ptr);
  2590                  }
  2591              } else
  2592              /* material chunk */
  2593              if(!memcmp(pe, "Material", 8)) {
  2594                  pe = _m3d_findarg(pe);
  2595                  if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
  2596                  pe = _m3d_safestr(pe, 0);
  2597                  if(!pe || !*pe) goto asciiend;
  2598                  for(i = 0; i < model->nummaterial; i++)
  2599                      if(!strcmp(pe, model->material[i].name)) {
  2600                          M3D_LOG("Multiple definitions for material");
  2601                          M3D_LOG(pe);
  2602                          M3D_FREE(pe);
  2603                          pe = NULL;
  2604                          while(*ptr && *ptr != '\r' && *ptr != '\n') ptr = _m3d_findnl(ptr);
  2605                          break;
  2606                      }
  2607                  if(!pe) continue;
  2608                  i = model->nummaterial++;
  2609                  if(model->flags & M3D_FLG_MTLLIB) {
  2610                      m = model->material;
  2611                      model->material = (m3dm_t*)M3D_MALLOC(model->nummaterial * sizeof(m3dm_t));
  2612                      if(!model->material) goto memerr;
  2613                      memcpy(model->material, m, (model->nummaterial - 1) * sizeof(m3dm_t));
  2614                      if(model->texture) {
  2615                          tx = model->texture;
  2616                          model->texture = (m3dtx_t*)M3D_MALLOC(model->numtexture * sizeof(m3dtx_t));
  2617                          if(!model->texture) goto memerr;
  2618                          memcpy(model->texture, tx, model->numtexture * sizeof(m3dm_t));
  2619                      }
  2620                      model->flags &= ~M3D_FLG_MTLLIB;
  2621                  } else {
  2622                      model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t));
  2623                      if(!model->material) goto memerr;
  2624                  }
  2625                  m = &model->material[i];
  2626                  m->name = pe;
  2627                  m->numprop = 0;
  2628                  m->prop = NULL;
  2629                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  2630                      k = n = 256;
  2631                      if(*ptr == 'm' && *(ptr+1) == 'a' && *(ptr+2) == 'p' && *(ptr+3) == '_') {
  2632                          k = m3dpf_map;
  2633                          ptr += 4;
  2634                      }
  2635                      for(j = 0; j < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); j++)
  2636                          if(!memcmp(ptr, m3d_propertytypes[j].key, strlen(m3d_propertytypes[j].key))) {
  2637                              n = m3d_propertytypes[j].id;
  2638                              if(k != m3dpf_map) k = m3d_propertytypes[j].format;
  2639                              break;
  2640                          }
  2641                      if(n != 256 && k != 256) {
  2642                          ptr = _m3d_findarg(ptr);
  2643                          if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2644                          j = m->numprop++;
  2645                          m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
  2646                          if(!m->prop) goto memerr;
  2647                          m->prop[j].type = n + (k == m3dpf_map && n < 128 ? 128 : 0);
  2648                          switch(k) {
  2649                              case m3dpf_color: ptr = _m3d_gethex(ptr, &m->prop[j].value.color); break;
  2650                              case m3dpf_uint8:
  2651                              case m3dpf_uint16:
  2652                              case m3dpf_uint32: ptr = _m3d_getint(ptr, &m->prop[j].value.num); break;
  2653                              case m3dpf_float:  ptr = _m3d_getfloat(ptr, &m->prop[j].value.fnum); break;
  2654                              case m3dpf_map:
  2655                                  pe = _m3d_safestr(ptr, 0);
  2656                                  if(!pe || !*pe) goto asciiend;
  2657                                  m->prop[j].value.textureid = _m3d_gettx(model, readfilecb, freecb, pe);
  2658                                  if(model->errcode == M3D_ERR_ALLOC) { M3D_FREE(pe); goto memerr; }
  2659                                  /* this error code only returned if readfilecb was specified */
  2660                                  if(m->prop[j].value.textureid == M3D_UNDEF) {
  2661                                      M3D_LOG("Texture not found");
  2662                                      M3D_LOG(pe);
  2663                                      m->numprop--;
  2664                                  }
  2665                                  M3D_FREE(pe);
  2666                              break;
  2667                          }
  2668                      } else {
  2669                          M3D_LOG("Unknown material property in");
  2670                          M3D_LOG(m->name);
  2671                          model->errcode = M3D_ERR_UNKPROP;
  2672                      }
  2673                      ptr = _m3d_findnl(ptr);
  2674                  }
  2675                  if(!m->numprop) model->nummaterial--;
  2676              } else
  2677              /* procedural */
  2678              if(!memcmp(pe, "Procedural", 10)) {
  2679                  pe = _m3d_safestr(ptr, 0);
  2680                  _m3d_getpr(model, readfilecb, freecb, pe);
  2681                  M3D_FREE(pe);
  2682                  while(*ptr && *ptr != '\r' && *ptr != '\n') ptr = _m3d_findnl(ptr);
  2683              } else
  2684              /* mesh */
  2685              if(!memcmp(pe, "Mesh", 4)) {
  2686                  mi = M3D_UNDEF;
  2687  #ifdef M3D_VERTEXMAX
  2688                  pi = M3D_UNDEF;
  2689  #endif
  2690                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  2691                      if(*ptr == 'u') {
  2692                          ptr = _m3d_findarg(ptr);
  2693                          if(!*ptr) goto asciiend;
  2694                          mi = M3D_UNDEF;
  2695                          if(*ptr != '\r' && *ptr != '\n') {
  2696                              pe = _m3d_safestr(ptr, 0);
  2697                              if(!pe || !*pe) goto asciiend;
  2698                              for(j = 0; j < model->nummaterial; j++)
  2699                                  if(!strcmp(pe, model->material[j].name)) { mi = (M3D_INDEX)j; break; }
  2700                              if(mi == M3D_UNDEF && !(model->flags & M3D_FLG_MTLLIB)) {
  2701                                  mi = model->nummaterial++;
  2702                                  model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t));
  2703                                  if(!model->material) goto memerr;
  2704                                  model->material[mi].name = pe;
  2705                                  model->material[mi].numprop = 1;
  2706                                  model->material[mi].prop = NULL;
  2707                              } else
  2708                                  M3D_FREE(pe);
  2709                          }
  2710                      } else
  2711                      if(*ptr == 'p') {
  2712                          ptr = _m3d_findarg(ptr);
  2713                          if(!*ptr) goto asciiend;
  2714  #ifdef M3D_VERTEXMAX
  2715                          pi = M3D_UNDEF;
  2716                          if(*ptr != '\r' && *ptr != '\n') {
  2717                              pe = _m3d_safestr(ptr, 0);
  2718                              if(!pe || !*pe) goto asciiend;
  2719                              for(j = 0; j < model->numparam; j++)
  2720                                  if(!strcmp(pe, model->param[j].name)) { pi = (M3D_INDEX)j; break; }
  2721                              if(pi == M3D_UNDEF) {
  2722                                  pi = model->numparam++;
  2723                                  model->param = (m3dvi_t*)M3D_REALLOC(model->param, model->numparam * sizeof(m3dvi_t));
  2724                                  if(!model->param) goto memerr;
  2725                                  model->param[pi].name = pe;
  2726                                  model->param[pi].count = 0;
  2727                              } else
  2728                                  M3D_FREE(pe);
  2729                          }
  2730  #endif
  2731                      } else {
  2732                          i = model->numface++;
  2733                          model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t));
  2734                          if(!model->face) goto memerr;
  2735                          memset(&model->face[i], 255, sizeof(m3df_t)); /* set all index to -1 by default */
  2736                          model->face[i].materialid = mi;
  2737  #ifdef M3D_VERTEXMAX
  2738                          model->face[i].paramid = pi;
  2739  #endif
  2740                          /* hardcoded triangles. */
  2741                          for(j = 0; j < 3; j++) {
  2742                              /* vertex */
  2743                              ptr = _m3d_getint(ptr, &k);
  2744                              model->face[i].vertex[j] = (M3D_INDEX)k;
  2745                              if(!*ptr) goto asciiend;
  2746                              if(*ptr == '/') {
  2747                                  ptr++;
  2748                                  if(*ptr != '/') {
  2749                                      /* texcoord */
  2750                                      ptr = _m3d_getint(ptr, &k);
  2751                                      model->face[i].texcoord[j] = (M3D_INDEX)k;
  2752                                      if(!*ptr) goto asciiend;
  2753                                  }
  2754                                  if(*ptr == '/') {
  2755                                      ptr++;
  2756                                      /* normal */
  2757                                      ptr = _m3d_getint(ptr, &k);
  2758                                      model->face[i].normal[j] = (M3D_INDEX)k;
  2759                                      if(!*ptr) goto asciiend;
  2760                                  }
  2761                                  if(*ptr == '/') {
  2762                                      ptr++;
  2763                                      /* maximum */
  2764                                      ptr = _m3d_getint(ptr, &k);
  2765  #ifdef M3D_VERTEXMAX
  2766                                      model->face[i].vertmax[j] = (M3D_INDEX)k;
  2767  #endif
  2768                                      if(!*ptr) goto asciiend;
  2769                                  }
  2770                              }
  2771  #ifndef M3D_NONORMALS
  2772                              if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1;
  2773  #endif
  2774                              ptr = _m3d_findarg(ptr);
  2775                          }
  2776                      }
  2777                      ptr = _m3d_findnl(ptr);
  2778                  }
  2779              } else
  2780              /* voxel types chunk */
  2781              if(!memcmp(pe, "VoxTypes", 8) || !memcmp(pe, "Voxtypes", 8)) {
  2782                  if(model->voxtype) { M3D_LOG("More voxel types chunks, should be unique"); goto asciiend; }
  2783                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  2784                      i = model->numvoxtype++;
  2785                      model->voxtype = (m3dvt_t*)M3D_REALLOC(model->voxtype, model->numvoxtype * sizeof(m3dvt_t));
  2786                      if(!model->voxtype) goto memerr;
  2787                      memset(&model->voxtype[i], 0, sizeof(m3dvt_t));
  2788                      model->voxtype[i].materialid = M3D_UNDEF;
  2789                      model->voxtype[i].skinid = M3D_UNDEF;
  2790                      ptr = _m3d_gethex(ptr, &model->voxtype[i].color);
  2791                      if(!*ptr) goto asciiend;
  2792                      if(*ptr == '/') {
  2793                          ptr = _m3d_gethex(ptr, &k);
  2794                          model->voxtype[i].rotation = k;
  2795                          if(!*ptr) goto asciiend;
  2796                          if(*ptr == '/') {
  2797                              ptr = _m3d_gethex(ptr, &k);
  2798                              model->voxtype[i].voxshape = k;
  2799                              if(!*ptr) goto asciiend;
  2800                          }
  2801                      }
  2802                      while(*ptr == ' ' || *ptr == '\t') ptr++;
  2803                      if(*ptr == '\r' || *ptr == '\n') { ptr = _m3d_findnl(ptr); continue; }
  2804                      /* name */
  2805                      if(*ptr != '-') {
  2806                          pe = _m3d_safestr(ptr, 0);
  2807                          if(!pe || !*pe) goto asciiend;
  2808                          model->voxtype[i].name = pe;
  2809                          for(j = 0; j < model->nummaterial; j++)
  2810                              if(!strcmp(pe, model->material[j].name)) { model->voxtype[i].materialid = (M3D_INDEX)j; break; }
  2811                      }
  2812                      ptr = _m3d_findarg(ptr);
  2813                      /* parse skin */
  2814                      memset(&s, 0, sizeof(m3ds_t));
  2815                      for(j = 0, w = (M3D_FLOAT)0.0; j < M3D_NUMBONE && *ptr && *ptr != '{' && *ptr != '\r' && *ptr != '\n'; j++) {
  2816                          ptr = _m3d_getint(ptr, &k);
  2817                          s.boneid[j] = (M3D_INDEX)k;
  2818                          if(*ptr == ':') {
  2819                              ptr++;
  2820                              ptr = _m3d_getfloat(ptr, &s.weight[j]);
  2821                              w += s.weight[j];
  2822                          } else if(!j)
  2823                              s.weight[j] = (M3D_FLOAT)1.0;
  2824                          if(!*ptr) goto asciiend;
  2825                          ptr = _m3d_findarg(ptr);
  2826                      }
  2827                      if(s.boneid[0] != M3D_UNDEF && s.weight[0] > (M3D_FLOAT)0.0) {
  2828                          if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0)
  2829                              for(j = 0; j < M3D_NUMBONE && s.weight[j] > (M3D_FLOAT)0.0; j++)
  2830                                  s.weight[j] /= w;
  2831                          k = M3D_NOTDEFINED;
  2832                          if(model->skin) {
  2833                              for(j = 0; j < model->numskin; j++)
  2834                                  if(!memcmp(&model->skin[j], &s, sizeof(m3ds_t))) { k = j; break; }
  2835                          }
  2836                          if(k == M3D_NOTDEFINED) {
  2837                              k = model->numskin++;
  2838                              model->skin = (m3ds_t*)M3D_REALLOC(model->skin, model->numskin * sizeof(m3ds_t));
  2839                              if(!model->skin) goto memerr;
  2840                              memcpy(&model->skin[k], &s, sizeof(m3ds_t));
  2841                          }
  2842                          model->voxtype[i].skinid = (M3D_INDEX)k;
  2843                      }
  2844                      /* parse item list */
  2845                      if(*ptr == '{') {
  2846                          while(*ptr == '{' || *ptr == ' ' || *ptr == '\t') ptr++;
  2847                          while(*ptr && *ptr != '}' && *ptr != '\r' && *ptr != '\n') {
  2848                              ptr = _m3d_getint(ptr, &k);
  2849                              ptr = _m3d_findarg(ptr);
  2850                              if(!*ptr || *ptr == '}' || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2851                              pe = _m3d_safestr(ptr, 0);
  2852                              if(!pe || !*pe) goto asciiend;
  2853                              ptr = _m3d_findarg(ptr);
  2854                              j = model->voxtype[i].numitem++;
  2855                              model->voxtype[i].item = (m3dvi_t*)M3D_REALLOC(model->voxtype[i].item,
  2856                                  model->voxtype[i].numitem * sizeof(m3dvi_t));
  2857                              if(!model->voxtype[i].item) goto memerr;
  2858                              model->voxtype[i].item[j].count = k;
  2859                              model->voxtype[i].item[j].name = pe;
  2860                          }
  2861                          if(*ptr != '}') goto asciiend;
  2862                      }
  2863                      ptr = _m3d_findnl(ptr);
  2864                  }
  2865              } else
  2866              /* voxel data */
  2867              if(!memcmp(pe, "Voxel", 5)) {
  2868                  if(!model->voxtype) { M3D_LOG("No voxel type chunk before voxel data"); goto asciiend; }
  2869                  pe = _m3d_findarg(pe);
  2870                  if(!*pe) goto asciiend;
  2871                  if(*pe == '\r' || *pe == '\n') pe = NULL;
  2872                  else pe = _m3d_safestr(pe, 0);
  2873                  i = model->numvoxel++;
  2874                  model->voxel = (m3dvx_t*)M3D_REALLOC(model->voxel, model->numvoxel * sizeof(m3dvx_t));
  2875                  if(!model->voxel) goto memerr;
  2876                  memset(&model->voxel[i], 0, sizeof(m3dvx_t));
  2877                  model->voxel[i].name = pe;
  2878                  k = l = 0;
  2879                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  2880                      switch(*ptr) {
  2881                          case 'u':
  2882                              ptr = _m3d_findarg(ptr);
  2883                              if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2884                              ptr = _m3d_getint(ptr, &n);
  2885                              model->voxel[i].uncertain = ((n > 0 && n < 256 ? n : 0) * 255) / 100;
  2886                              ptr = _m3d_findarg(ptr);
  2887                              if(*ptr && *ptr != '\r' && *ptr != '\n') {
  2888                                  ptr = _m3d_getint(ptr, &n);
  2889                                  model->voxel[i].groupid = n > 0 && n < 256 ? n : 0;
  2890                              }
  2891                          break;
  2892                          case 'p':
  2893                              ptr = _m3d_findarg(ptr);
  2894                              if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2895                              ptr = _m3d_getint(ptr, &n);
  2896                              model->voxel[i].x = n;
  2897                              ptr = _m3d_findarg(ptr);
  2898                              if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2899                              ptr = _m3d_getint(ptr, &n);
  2900                              model->voxel[i].y = n;
  2901                              ptr = _m3d_findarg(ptr);
  2902                              if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2903                              ptr = _m3d_getint(ptr, &n);
  2904                              model->voxel[i].z = n;
  2905                          break;
  2906                          case 'd':
  2907                              ptr = _m3d_findarg(ptr);
  2908                              if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2909                              ptr = _m3d_getint(ptr, &n);
  2910                              model->voxel[i].w = n;
  2911                              ptr = _m3d_findarg(ptr);
  2912                              if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2913                              ptr = _m3d_getint(ptr, &n);
  2914                              model->voxel[i].h = n;
  2915                              ptr = _m3d_findarg(ptr);
  2916                              if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  2917                              ptr = _m3d_getint(ptr, &n);
  2918                              model->voxel[i].d = n;
  2919                          break;
  2920                          case 'l':
  2921                              if(model->voxel[i].data) { l++; k = 0; }
  2922                              else {
  2923                                  if(!model->voxel[i].w || !model->voxel[i].h || !model->voxel[i].d) {
  2924                                      M3D_LOG("No voxel dimension before layer data");
  2925                                      goto asciiend;
  2926                                  }
  2927                                  model->voxel[i].data = (M3D_VOXEL*)M3D_MALLOC(
  2928                                      model->voxel[i].w * model->voxel[i].h * model->voxel[i].d * sizeof(M3D_VOXEL));
  2929                                  if(!model->voxel[i].data) goto memerr;
  2930                              }
  2931                          break;
  2932                          default:
  2933                              if(!model->voxel[i].data || l >= model->voxel[i].h || k >= model->voxel[i].d) {
  2934                                  M3D_LOG("Missing voxel attributes or out of bound data");
  2935                                  goto asciiend;
  2936                              }
  2937                              for(n = l * model->voxel[i].w * model->voxel[i].d + k * model->voxel[i].w;
  2938                                  j < model->voxel[i].w && *ptr && *ptr != '\r' && *ptr != '\n'; j++) {
  2939                                  ptr = _m3d_getint(ptr, &am);
  2940                                  if(am >= model->numvoxtype) goto asciiend;
  2941                                  model->voxel[i].data[n + j] = am;
  2942                              }
  2943                              k++;
  2944                          break;
  2945                      }
  2946                      ptr = _m3d_findnl(ptr);
  2947                  }
  2948              } else
  2949              /* mathematical shape */
  2950              if(!memcmp(pe, "Shape", 5)) {
  2951                  pe = _m3d_findarg(pe);
  2952                  if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
  2953                  pe = _m3d_safestr(pe, 0);
  2954                  if(!pe || !*pe) goto asciiend;
  2955                  i = model->numshape++;
  2956                  model->shape = (m3dh_t*)M3D_REALLOC(model->shape, model->numshape * sizeof(m3ds_t));
  2957                  if(!model->shape) goto memerr;
  2958                  h = &model->shape[i];
  2959                  h->name = pe;
  2960                  h->group = M3D_UNDEF;
  2961                  h->numcmd = 0;
  2962                  h->cmd = NULL;
  2963                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  2964                      if(!memcmp(ptr, "group", 5)) {
  2965                          ptr = _m3d_findarg(ptr);
  2966                          ptr = _m3d_getint(ptr, &h->group);
  2967                          ptr = _m3d_findnl(ptr);
  2968                          if(h->group != M3D_UNDEF && h->group >= model->numbone) {
  2969                              M3D_LOG("Unknown bone id as shape group in shape");
  2970                              M3D_LOG(pe);
  2971                              h->group = M3D_UNDEF;
  2972                              model->errcode = M3D_ERR_SHPE;
  2973                          }
  2974                          continue;
  2975                      }
  2976                      for(cd = NULL, k = 0; k < (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])); k++) {
  2977                          j = (unsigned int)strlen(m3d_commandtypes[k].key);
  2978                          if(!memcmp(ptr, m3d_commandtypes[k].key, j) && (ptr[j] == ' ' || ptr[j] == '\r' || ptr[j] == '\n'))
  2979                              { cd = &m3d_commandtypes[k]; break; }
  2980                      }
  2981                      if(cd) {
  2982                          j = h->numcmd++;
  2983                          h->cmd = (m3dc_t*)M3D_REALLOC(h->cmd, h->numcmd * sizeof(m3dc_t));
  2984                          if(!h->cmd) goto memerr;
  2985                          h->cmd[j].type = k;
  2986                          h->cmd[j].arg = (uint32_t*)M3D_MALLOC(cd->p * sizeof(uint32_t));
  2987                          if(!h->cmd[j].arg) goto memerr;
  2988                          memset(h->cmd[j].arg, 0, cd->p * sizeof(uint32_t));
  2989                          for(k = n = 0, l = cd->p; k < l; k++) {
  2990                              ptr = _m3d_findarg(ptr);
  2991                              if(!*ptr) goto asciiend;
  2992                              if(*ptr == '[') {
  2993                                  ptr = _m3d_findarg(ptr + 1);
  2994                                  if(!*ptr) goto asciiend;
  2995                              }
  2996                              if(*ptr == ']' || *ptr == '\r' || *ptr == '\n') break;
  2997                              switch(cd->a[((k - n) % (cd->p - n)) + n]) {
  2998                                  case m3dcp_mi_t:
  2999                                      mi = M3D_UNDEF;
  3000                                      if(*ptr != '\r' && *ptr != '\n') {
  3001                                          pe = _m3d_safestr(ptr, 0);
  3002                                          if(!pe || !*pe) goto asciiend;
  3003                                          for(n = 0; n < model->nummaterial; n++)
  3004                                              if(!strcmp(pe, model->material[n].name)) { mi = (M3D_INDEX)n; break; }
  3005                                          if(mi == M3D_UNDEF && !(model->flags & M3D_FLG_MTLLIB)) {
  3006                                              mi = model->nummaterial++;
  3007                                              model->material = (m3dm_t*)M3D_REALLOC(model->material,
  3008                                                  model->nummaterial * sizeof(m3dm_t));
  3009                                              if(!model->material) goto memerr;
  3010                                              model->material[mi].name = pe;
  3011                                              model->material[mi].numprop = 1;
  3012                                              model->material[mi].prop = NULL;
  3013                                          } else
  3014                                              M3D_FREE(pe);
  3015                                      }
  3016                                      h->cmd[j].arg[k] = mi;
  3017                                  break;
  3018                                  case m3dcp_vc_t:
  3019  #ifdef M3D_DOUBLE
  3020                                      _m3d_getfloat(ptr, &w); f = w;
  3021                                      memcpy(&h->cmd[j].arg[k], &f, 4);
  3022  #else
  3023                                      _m3d_getfloat(ptr, (float*)&h->cmd[j].arg[k]);
  3024  #endif
  3025                                  break;
  3026                                  case m3dcp_va_t:
  3027                                      ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]);
  3028                                      n = k + 1; l += (h->cmd[j].arg[k] - 1) * (cd->p - k - 1);
  3029                                      h->cmd[j].arg = (uint32_t*)M3D_REALLOC(h->cmd[j].arg, l * sizeof(uint32_t));
  3030                                      if(!h->cmd[j].arg) goto memerr;
  3031                                      memset(&h->cmd[j].arg[k + 1], 0, (l - k - 1) * sizeof(uint32_t));
  3032                                  break;
  3033                                  case m3dcp_qi_t:
  3034                                      ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]);
  3035                                      model->vertex[h->cmd[i].arg[k]].skinid = M3D_INDEXMAX;
  3036                                  break;
  3037                                  default:
  3038                                      ptr = _m3d_getint(ptr, &h->cmd[j].arg[k]);
  3039                                  break;
  3040                              }
  3041                          }
  3042                      } else {
  3043                          M3D_LOG("Unknown shape command in");
  3044                          M3D_LOG(h->name);
  3045                          model->errcode = M3D_ERR_UNKCMD;
  3046                      }
  3047                      ptr = _m3d_findnl(ptr);
  3048                  }
  3049                  if(!h->numcmd) model->numshape--;
  3050              } else
  3051              /* annotation labels */
  3052              if(!memcmp(pe, "Labels", 6)) {
  3053                  pe = _m3d_findarg(pe);
  3054                  if(!*pe) goto asciiend;
  3055                  if(*pe == '\r' || *pe == '\n') pe = NULL;
  3056                  else pe = _m3d_safestr(pe, 0);
  3057                  k = 0; fn = NULL;
  3058                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  3059                      if(*ptr == 'c') {
  3060                          ptr = _m3d_findarg(ptr);
  3061                          if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
  3062                          ptr = _m3d_gethex(ptr, &k);
  3063                      } else
  3064                      if(*ptr == 'l') {
  3065                          ptr = _m3d_findarg(ptr);
  3066                          if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
  3067                          fn = _m3d_safestr(ptr, 2);
  3068                      } else {
  3069                          i = model->numlabel++;
  3070                          model->label = (m3dl_t*)M3D_REALLOC(model->label, model->numlabel * sizeof(m3dl_t));
  3071                          if(!model->label) goto memerr;
  3072                          model->label[i].name = pe;
  3073                          model->label[i].lang = fn;
  3074                          model->label[i].color = k;
  3075                          ptr = _m3d_getint(ptr, &j);
  3076                          model->label[i].vertexid = (M3D_INDEX)j;
  3077                          ptr = _m3d_findarg(ptr);
  3078                          if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
  3079                          model->label[i].text = _m3d_safestr(ptr, 2);
  3080                      }
  3081                      ptr = _m3d_findnl(ptr);
  3082                  }
  3083              } else
  3084              /* action */
  3085              if(!memcmp(pe, "Action", 6)) {
  3086                  pe = _m3d_findarg(pe);
  3087                  if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
  3088                  pe = _m3d_getint(pe, &k);
  3089                  pe = _m3d_findarg(pe);
  3090                  if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
  3091                  pe = _m3d_safestr(pe, 0);
  3092                  if(!pe || !*pe) goto asciiend;
  3093                  i = model->numaction++;
  3094                  model->action = (m3da_t*)M3D_REALLOC(model->action, model->numaction * sizeof(m3da_t));
  3095                  if(!model->action) goto memerr;
  3096                  a = &model->action[i];
  3097                  a->name = pe;
  3098                  a->durationmsec = k;
  3099                  /* skip the first frame marker as there's always at least one frame */
  3100                  a->numframe = 1;
  3101                  a->frame = (m3dfr_t*)M3D_MALLOC(sizeof(m3dfr_t));
  3102                  if(!a->frame) goto memerr;
  3103                  a->frame[0].msec = 0;
  3104                  a->frame[0].numtransform = 0;
  3105                  a->frame[0].transform = NULL;
  3106                  i = 0;
  3107                  if(*ptr == 'f')
  3108                      ptr = _m3d_findnl(ptr);
  3109                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  3110                      if(*ptr == 'f') {
  3111                          i = a->numframe++;
  3112                          a->frame = (m3dfr_t*)M3D_REALLOC(a->frame, a->numframe * sizeof(m3dfr_t));
  3113                          if(!a->frame) goto memerr;
  3114                          ptr = _m3d_findarg(ptr);
  3115                          ptr = _m3d_getint(ptr, &a->frame[i].msec);
  3116                          a->frame[i].numtransform = 0;
  3117                          a->frame[i].transform = NULL;
  3118                      } else {
  3119                          j = a->frame[i].numtransform++;
  3120                          a->frame[i].transform = (m3dtr_t*)M3D_REALLOC(a->frame[i].transform,
  3121                              a->frame[i].numtransform * sizeof(m3dtr_t));
  3122                          if(!a->frame[i].transform) goto memerr;
  3123                          ptr = _m3d_getint(ptr, &k);
  3124                          ptr = _m3d_findarg(ptr);
  3125                          if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  3126                          a->frame[i].transform[j].boneid = (M3D_INDEX)k;
  3127                          ptr = _m3d_getint(ptr, &k);
  3128                          ptr = _m3d_findarg(ptr);
  3129                          if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  3130                          a->frame[i].transform[j].pos = (M3D_INDEX)k;
  3131                          ptr = _m3d_getint(ptr, &k);
  3132                          if(!*ptr || *ptr == '\r' || *ptr == '\n') goto asciiend;
  3133                          a->frame[i].transform[j].ori = (M3D_INDEX)k;
  3134                          model->vertex[k].skinid = M3D_INDEXMAX;
  3135                      }
  3136                      ptr = _m3d_findnl(ptr);
  3137                  }
  3138              } else
  3139              /* inlined assets chunk */
  3140              if(!memcmp(pe, "Assets", 6)) {
  3141                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  3142                      if(readfilecb) {
  3143                          pe = _m3d_safestr(ptr, 2);
  3144                          if(!pe || !*pe) goto asciiend;
  3145                          i = model->numinlined++;
  3146                          model->inlined = (m3di_t*)M3D_REALLOC(model->inlined, model->numinlined * sizeof(m3di_t));
  3147                          if(!model->inlined) goto memerr;
  3148                          t = &model->inlined[i];
  3149                          model->inlined[i].data = (*readfilecb)(pe, &model->inlined[i].length);
  3150                          if(model->inlined[i].data) {
  3151                              fn = strrchr(pe, '.');
  3152                              if(fn && (fn[1] == 'p' || fn[1] == 'P') && (fn[2] == 'n' || fn[2] == 'N') &&
  3153                                  (fn[3] == 'g' || fn[3] == 'G')) *fn = 0;
  3154                              fn = strrchr(pe, '/');
  3155                              if(!fn) fn = strrchr(pe, '\\');
  3156                              if(!fn) fn = pe; else fn++;
  3157                              model->inlined[i].name = _m3d_safestr(fn, 0);
  3158                          } else
  3159                              model->numinlined--;
  3160                          M3D_FREE(pe);
  3161                      }
  3162                      ptr = _m3d_findnl(ptr);
  3163                  }
  3164              } else
  3165              /* extra chunks */
  3166              if(!memcmp(pe, "Extra", 5)) {
  3167                  pe = _m3d_findarg(pe);
  3168                  if(!*pe || *pe == '\r' || *pe == '\n') goto asciiend;
  3169                  buff = (unsigned char*)_m3d_findnl(ptr);
  3170                  k = ((uint32_t)((uintptr_t)buff - (uintptr_t)ptr) / 3) + 1;
  3171                  i = model->numextra++;
  3172                  model->extra = (m3dchunk_t**)M3D_REALLOC(model->extra, model->numextra * sizeof(m3dchunk_t*));
  3173                  if(!model->extra) goto memerr;
  3174                  model->extra[i] = (m3dchunk_t*)M3D_MALLOC(k + sizeof(m3dchunk_t));
  3175                  if(!model->extra[i]) goto memerr;
  3176                  memcpy(&model->extra[i]->magic, pe, 4);
  3177                  model->extra[i]->length = sizeof(m3dchunk_t);
  3178                  pe = (char*)model->extra[i] + sizeof(m3dchunk_t);
  3179                  while(*ptr && *ptr != '\r' && *ptr != '\n') {
  3180                      ptr = _m3d_gethex(ptr, &k);
  3181                      *pe++ = (uint8_t)k;
  3182                      model->extra[i]->length++;
  3183                  }
  3184              } else
  3185                  goto asciiend;
  3186          }
  3187          model->errcode = M3D_SUCCESS;
  3188  asciiend:
  3189          setlocale(LC_NUMERIC, ol);
  3190          goto postprocess;
  3191      }
  3192  #endif
  3193      /* Binary variant */
  3194      len = ((m3dhdr_t*)data)->length - 8;
  3195      data += 8;
  3196      if(M3D_CHUNKMAGIC(data, 'P','R','V','W')) {
  3197          /* optional preview chunk */
  3198          model->preview.length = ((m3dchunk_t*)data)->length;
  3199          model->preview.data = data + sizeof(m3dchunk_t);
  3200          data += model->preview.length;
  3201          len -= model->preview.length;
  3202      }
  3203      if(!M3D_CHUNKMAGIC(data, 'H','E','A','D')) {
  3204          buff = (unsigned char *)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)data, len, 4096, (int*)&len, 1);
  3205          if(!buff || !len || !M3D_CHUNKMAGIC(buff, 'H','E','A','D')) {
  3206              if(buff) M3D_FREE(buff);
  3207              M3D_FREE(model);
  3208              return NULL;
  3209          }
  3210          buff = (unsigned char*)M3D_REALLOC(buff, len);
  3211          model->flags |= M3D_FLG_FREERAW; /* mark that we have to free the raw buffer */
  3212          data = buff;
  3213  #ifdef M3D_PROFILING
  3214          gettimeofday(&tv1, NULL);
  3215          tvd.tv_sec = tv1.tv_sec - tv0.tv_sec;
  3216          tvd.tv_usec = tv1.tv_usec - tv0.tv_usec;
  3217          if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
  3218          printf("  Deflate model   %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec);
  3219          memcpy(&tv0, &tv1, sizeof(struct timeval));
  3220  #endif
  3221      }
  3222      model->raw = (m3dhdr_t*)data;
  3223      end = data + len;
  3224  
  3225      /* parse header */
  3226      data += sizeof(m3dhdr_t);
  3227      M3D_LOG((char*)data);
  3228      model->name = (char*)data;
  3229      for(; data < end && *data; data++) {}; data++;
  3230      model->license = (char*)data;
  3231      for(; data < end && *data; data++) {}; data++;
  3232      model->author = (char*)data;
  3233      for(; data < end && *data; data++) {}; data++;
  3234      model->desc = (char*)data;
  3235      chunk = (unsigned char*)model->raw + model->raw->length;
  3236      model->scale = (M3D_FLOAT)model->raw->scale;
  3237      if(model->scale <= (M3D_FLOAT)0.0) model->scale = (M3D_FLOAT)1.0;
  3238      model->vc_s = 1 << ((model->raw->types >> 0) & 3);  /* vertex coordinate size */
  3239      model->vi_s = 1 << ((model->raw->types >> 2) & 3);  /* vertex index size */
  3240      model->si_s = 1 << ((model->raw->types >> 4) & 3);  /* string offset size */
  3241      model->ci_s = 1 << ((model->raw->types >> 6) & 3);  /* color index size */
  3242      model->ti_s = 1 << ((model->raw->types >> 8) & 3);  /* tmap index size */
  3243      model->bi_s = 1 << ((model->raw->types >>10) & 3);  /* bone index size */
  3244      model->nb_s = 1 << ((model->raw->types >>12) & 3);  /* number of bones per vertex */
  3245      model->sk_s = 1 << ((model->raw->types >>14) & 3);  /* skin index size */
  3246      model->fc_s = 1 << ((model->raw->types >>16) & 3);  /* frame counter size */
  3247      model->hi_s = 1 << ((model->raw->types >>18) & 3);  /* shape index size */
  3248      model->fi_s = 1 << ((model->raw->types >>20) & 3);  /* face index size */
  3249      model->vd_s = 1 << ((model->raw->types >>22) & 3);  /* voxel dimension size */
  3250      model->vp_s = 1 << ((model->raw->types >>24) & 3);  /* voxel pixel size */
  3251      if(model->ci_s == 8) model->ci_s = 0;               /* optional indices */
  3252      if(model->ti_s == 8) model->ti_s = 0;
  3253      if(model->bi_s == 8) model->bi_s = 0;
  3254      if(model->sk_s == 8) model->sk_s = 0;
  3255      if(model->fc_s == 8) model->fc_s = 0;
  3256      if(model->hi_s == 8) model->hi_s = 0;
  3257      if(model->fi_s == 8) model->fi_s = 0;
  3258  
  3259      /* variable limit checks */
  3260      if(sizeof(M3D_FLOAT) == 4 && model->vc_s > 4) {
  3261          M3D_LOG("Double precision coordinates not supported, truncating to float...");
  3262          model->errcode = M3D_ERR_TRUNC;
  3263      }
  3264      if((sizeof(M3D_INDEX) == 2 && (model->vi_s > 2 || model->si_s > 2 || model->ci_s > 2 || model->ti_s > 2 ||
  3265          model->bi_s > 2 || model->sk_s > 2 || model->fc_s > 2 || model->hi_s > 2 || model->fi_s > 2)) ||
  3266         (sizeof(M3D_VOXEL) < (size_t)model->vp_s && model->vp_s != 8)) {
  3267          M3D_LOG("32 bit indices not supported, unable to load model");
  3268          M3D_FREE(model);
  3269          return NULL;
  3270      }
  3271      if(model->vi_s > 4 || model->si_s > 4 || model->vp_s == 4) {
  3272          M3D_LOG("Invalid index size, unable to load model");
  3273          M3D_FREE(model);
  3274          return NULL;
  3275      }
  3276      if(!M3D_CHUNKMAGIC(end - 4, 'O','M','D','3')) {
  3277          M3D_LOG("Missing end chunk");
  3278          M3D_FREE(model);
  3279          return NULL;
  3280      }
  3281      if(model->nb_s > M3D_NUMBONE) {
  3282          M3D_LOG("Model has more bones per vertex than what importer was configured to support");
  3283          model->errcode = M3D_ERR_TRUNC;
  3284      }
  3285  
  3286      /* look for inlined assets in advance, material and procedural chunks may need them */
  3287      buff = chunk;
  3288      while(buff < end && !M3D_CHUNKMAGIC(buff, 'O','M','D','3')) {
  3289          data = buff;
  3290          len = ((m3dchunk_t*)data)->length;
  3291          buff += len;
  3292          if(len < sizeof(m3dchunk_t) || buff >= end) {
  3293              M3D_LOG("Invalid chunk size");
  3294              break;
  3295          }
  3296          len -= sizeof(m3dchunk_t) + model->si_s;
  3297  
  3298          /* inlined assets */
  3299          if(M3D_CHUNKMAGIC(data, 'A','S','E','T') && len > 0) {
  3300              M3D_LOG("Inlined asset");
  3301              i = model->numinlined++;
  3302              model->inlined = (m3di_t*)M3D_REALLOC(model->inlined, model->numinlined * sizeof(m3di_t));
  3303              if(!model->inlined) {
  3304  memerr:         M3D_LOG("Out of memory");
  3305                  model->errcode = M3D_ERR_ALLOC;
  3306                  return model;
  3307              }
  3308              data += sizeof(m3dchunk_t);
  3309              t = &model->inlined[i];
  3310              M3D_GETSTR(t->name);
  3311              M3D_LOG(t->name);
  3312              t->data = (uint8_t*)data;
  3313              t->length = len;
  3314          }
  3315      }
  3316  
  3317      /* parse chunks */
  3318      while(chunk < end && !M3D_CHUNKMAGIC(chunk, 'O','M','D','3')) {
  3319          data = chunk;
  3320          len = ((m3dchunk_t*)chunk)->length;
  3321          chunk += len;
  3322          if(len < sizeof(m3dchunk_t) || chunk >= end) {
  3323              M3D_LOG("Invalid chunk size");
  3324              break;
  3325          }
  3326          len -= sizeof(m3dchunk_t);
  3327  
  3328          /* color map */
  3329          if(M3D_CHUNKMAGIC(data, 'C','M','A','P')) {
  3330              M3D_LOG("Color map");
  3331              if(model->cmap) { M3D_LOG("More color map chunks, should be unique"); model->errcode = M3D_ERR_CMAP; continue; }
  3332              if(!model->ci_s) { M3D_LOG("Color map chunk, shouldn't be any"); model->errcode = M3D_ERR_CMAP; continue; }
  3333              model->numcmap = len / sizeof(uint32_t);
  3334              model->cmap = (uint32_t*)(data + sizeof(m3dchunk_t));
  3335          } else
  3336          /* texture map */
  3337          if(M3D_CHUNKMAGIC(data, 'T','M','A','P')) {
  3338              M3D_LOG("Texture map");
  3339              if(model->tmap) { M3D_LOG("More texture map chunks, should be unique"); model->errcode = M3D_ERR_TMAP; continue; }
  3340              if(!model->ti_s) { M3D_LOG("Texture map chunk, shouldn't be any"); model->errcode = M3D_ERR_TMAP; continue; }
  3341              reclen = model->vc_s + model->vc_s;
  3342              model->numtmap = len / reclen;
  3343              model->tmap = (m3dti_t*)M3D_MALLOC(model->numtmap * sizeof(m3dti_t));
  3344              if(!model->tmap) goto memerr;
  3345              for(i = 0, data += sizeof(m3dchunk_t); data < chunk; i++) {
  3346                  switch(model->vc_s) {
  3347                      case 1:
  3348                          model->tmap[i].u = (M3D_FLOAT)((uint8_t)data[0]) / (M3D_FLOAT)255.0;
  3349                          model->tmap[i].v = (M3D_FLOAT)((uint8_t)data[1]) / (M3D_FLOAT)255.0;
  3350                      break;
  3351                      case 2:
  3352                          model->tmap[i].u = (M3D_FLOAT)(*((uint16_t*)(data+0))) / (M3D_FLOAT)65535.0;
  3353                          model->tmap[i].v = (M3D_FLOAT)(*((uint16_t*)(data+2))) / (M3D_FLOAT)65535.0;
  3354                      break;
  3355                      case 4:
  3356                          model->tmap[i].u = (M3D_FLOAT)(*((float*)(data+0)));
  3357                          model->tmap[i].v = (M3D_FLOAT)(*((float*)(data+4)));
  3358                      break;
  3359                      case 8:
  3360                          model->tmap[i].u = (M3D_FLOAT)(*((double*)(data+0)));
  3361                          model->tmap[i].v = (M3D_FLOAT)(*((double*)(data+8)));
  3362                      break;
  3363                  }
  3364                  data += reclen;
  3365              }
  3366          } else
  3367          /* vertex list */
  3368          if(M3D_CHUNKMAGIC(data, 'V','R','T','S')) {
  3369              M3D_LOG("Vertex list");
  3370              if(model->vertex) { M3D_LOG("More vertex chunks, should be unique"); model->errcode = M3D_ERR_VRTS; continue; }
  3371              if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP;
  3372              reclen = model->ci_s + model->sk_s + 4 * model->vc_s;
  3373              model->numvertex = len / reclen;
  3374              model->vertex = (m3dv_t*)M3D_MALLOC(model->numvertex * sizeof(m3dv_t));
  3375              if(!model->vertex) goto memerr;
  3376              memset(model->vertex, 0, model->numvertex * sizeof(m3dv_t));
  3377              for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < model->numvertex; i++) {
  3378                  switch(model->vc_s) {
  3379                      case 1:
  3380                          model->vertex[i].x = (M3D_FLOAT)((int8_t)data[0]) / (M3D_FLOAT)127.0;
  3381                          model->vertex[i].y = (M3D_FLOAT)((int8_t)data[1]) / (M3D_FLOAT)127.0;
  3382                          model->vertex[i].z = (M3D_FLOAT)((int8_t)data[2]) / (M3D_FLOAT)127.0;
  3383                          model->vertex[i].w = (M3D_FLOAT)((int8_t)data[3]) / (M3D_FLOAT)127.0;
  3384                          data += 4;
  3385                      break;
  3386                      case 2:
  3387                          model->vertex[i].x = (M3D_FLOAT)(*((int16_t*)(data+0))) / (M3D_FLOAT)32767.0;
  3388                          model->vertex[i].y = (M3D_FLOAT)(*((int16_t*)(data+2))) / (M3D_FLOAT)32767.0;
  3389                          model->vertex[i].z = (M3D_FLOAT)(*((int16_t*)(data+4))) / (M3D_FLOAT)32767.0;
  3390                          model->vertex[i].w = (M3D_FLOAT)(*((int16_t*)(data+6))) / (M3D_FLOAT)32767.0;
  3391                          data += 8;
  3392                      break;
  3393                      case 4:
  3394                          model->vertex[i].x = (M3D_FLOAT)(*((float*)(data+0)));
  3395                          model->vertex[i].y = (M3D_FLOAT)(*((float*)(data+4)));
  3396                          model->vertex[i].z = (M3D_FLOAT)(*((float*)(data+8)));
  3397                          model->vertex[i].w = (M3D_FLOAT)(*((float*)(data+12)));
  3398                          data += 16;
  3399                      break;
  3400                      case 8:
  3401                          model->vertex[i].x = (M3D_FLOAT)(*((double*)(data+0)));
  3402                          model->vertex[i].y = (M3D_FLOAT)(*((double*)(data+8)));
  3403                          model->vertex[i].z = (M3D_FLOAT)(*((double*)(data+16)));
  3404                          model->vertex[i].w = (M3D_FLOAT)(*((double*)(data+24)));
  3405                          data += 32;
  3406                      break;
  3407                  }
  3408                  switch(model->ci_s) {
  3409                      case 1: model->vertex[i].color = model->cmap ? model->cmap[data[0]] : 0; data++; break;
  3410                      case 2: model->vertex[i].color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
  3411                      case 4: model->vertex[i].color = *((uint32_t*)data); data += 4; break;
  3412                      /* case 8: break; */
  3413                  }
  3414                  model->vertex[i].skinid = M3D_UNDEF;
  3415                  data = _m3d_getidx(data, model->sk_s, &model->vertex[i].skinid);
  3416              }
  3417          } else
  3418          /* skeleton: bone hierarchy and skin */
  3419          if(M3D_CHUNKMAGIC(data, 'B','O','N','E')) {
  3420              M3D_LOG("Skeleton");
  3421              if(model->bone) { M3D_LOG("More bone chunks, should be unique"); model->errcode = M3D_ERR_BONE; continue; }
  3422              if(!model->bi_s) { M3D_LOG("Bone chunk, shouldn't be any"); model->errcode=M3D_ERR_BONE; continue; }
  3423              if(!model->vertex) { M3D_LOG("No vertex chunk before bones"); model->errcode = M3D_ERR_VRTS; break; }
  3424              data += sizeof(m3dchunk_t);
  3425              model->numbone = 0;
  3426              data = _m3d_getidx(data, model->bi_s, &model->numbone);
  3427              if(model->numbone) {
  3428                  model->bone = (m3db_t*)M3D_MALLOC(model->numbone * sizeof(m3db_t));
  3429                  if(!model->bone) goto memerr;
  3430              }
  3431              model->numskin = 0;
  3432              data = _m3d_getidx(data, model->sk_s, &model->numskin);
  3433              /* read bone hierarchy */
  3434              for(i = 0; data < chunk && i < model->numbone; i++) {
  3435                  data = _m3d_getidx(data, model->bi_s, &model->bone[i].parent);
  3436                  M3D_GETSTR(model->bone[i].name);
  3437                  data = _m3d_getidx(data, model->vi_s, &model->bone[i].pos);
  3438                  data = _m3d_getidx(data, model->vi_s, &model->bone[i].ori);
  3439                  model->bone[i].numweight = 0;
  3440                  model->bone[i].weight = NULL;
  3441              }
  3442              /* read skin definitions */
  3443              if(model->numskin) {
  3444                  model->skin = (m3ds_t*)M3D_MALLOC(model->numskin * sizeof(m3ds_t));
  3445                  if(!model->skin) goto memerr;
  3446                  for(i = 0; data < chunk && i < model->numskin; i++) {
  3447                      for(j = 0; j < M3D_NUMBONE; j++) {
  3448                          model->skin[i].boneid[j] = M3D_UNDEF;
  3449                          model->skin[i].weight[j] = (M3D_FLOAT)0.0;
  3450                      }
  3451                      memset(&weights, 0, sizeof(weights));
  3452                      if(model->nb_s == 1) weights[0] = 255;
  3453                      else {
  3454                          memcpy(&weights, data, model->nb_s);
  3455                          data += model->nb_s;
  3456                      }
  3457                      for(j = 0, w = (M3D_FLOAT)0.0; j < (unsigned int)model->nb_s; j++) {
  3458                          if(weights[j]) {
  3459                              if(j >= M3D_NUMBONE)
  3460                                  data += model->bi_s;
  3461                              else {
  3462                                  model->skin[i].weight[j] = (M3D_FLOAT)(weights[j]) / (M3D_FLOAT)255.0;
  3463                                  w += model->skin[i].weight[j];
  3464                                  data = _m3d_getidx(data, model->bi_s, &model->skin[i].boneid[j]);
  3465                              }
  3466                          }
  3467                      }
  3468                      /* this can occur if model has more bones than what the importer is configured to handle */
  3469                      if(w != (M3D_FLOAT)1.0 && w != (M3D_FLOAT)0.0) {
  3470                          for(j = 0; j < M3D_NUMBONE; j++)
  3471                              model->skin[i].weight[j] /= w;
  3472                      }
  3473                  }
  3474              }
  3475          } else
  3476          /* material */
  3477          if(M3D_CHUNKMAGIC(data, 'M','T','R','L')) {
  3478              data += sizeof(m3dchunk_t);
  3479              M3D_GETSTR(name);
  3480              M3D_LOG("Material");
  3481              M3D_LOG(name);
  3482              if(model->ci_s < 4 && !model->numcmap) model->errcode = M3D_ERR_CMAP;
  3483              for(i = 0; i < model->nummaterial; i++)
  3484                  if(!strcmp(name, model->material[i].name)) {
  3485                      model->errcode = M3D_ERR_MTRL;
  3486                      M3D_LOG("Multiple definitions for material");
  3487                      M3D_LOG(name);
  3488                      name = NULL;
  3489                      break;
  3490                  }
  3491              if(name) {
  3492                  i = model->nummaterial++;
  3493                  if(model->flags & M3D_FLG_MTLLIB) {
  3494                      m = model->material;
  3495                      model->material = (m3dm_t*)M3D_MALLOC(model->nummaterial * sizeof(m3dm_t));
  3496                      if(!model->material) goto memerr;
  3497                      memcpy(model->material, m, (model->nummaterial - 1) * sizeof(m3dm_t));
  3498                      if(model->texture) {
  3499                          tx = model->texture;
  3500                          model->texture = (m3dtx_t*)M3D_MALLOC(model->numtexture * sizeof(m3dtx_t));
  3501                          if(!model->texture) goto memerr;
  3502                          memcpy(model->texture, tx, model->numtexture * sizeof(m3dm_t));
  3503                      }
  3504                      model->flags &= ~M3D_FLG_MTLLIB;
  3505                  } else {
  3506                      model->material = (m3dm_t*)M3D_REALLOC(model->material, model->nummaterial * sizeof(m3dm_t));
  3507                      if(!model->material) goto memerr;
  3508                  }
  3509                  m = &model->material[i];
  3510                  m->numprop = 0;
  3511                  m->name = name;
  3512                  m->prop = (m3dp_t*)M3D_MALLOC((len / 2) * sizeof(m3dp_t));
  3513                  if(!m->prop) goto memerr;
  3514                  while(data < chunk) {
  3515                      i = m->numprop++;
  3516                      m->prop[i].type = *data++;
  3517                      m->prop[i].value.num = 0;
  3518                      if(m->prop[i].type >= 128)
  3519                          k = m3dpf_map;
  3520                      else {
  3521                          for(k = 256, j = 0; j < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); j++)
  3522                              if(m->prop[i].type == m3d_propertytypes[j].id) { k = m3d_propertytypes[j].format; break; }
  3523                      }
  3524                      switch(k) {
  3525                          case m3dpf_color:
  3526                              switch(model->ci_s) {
  3527                                  case 1: m->prop[i].value.color = model->cmap ? model->cmap[data[0]] : 0; data++; break;
  3528                                  case 2: m->prop[i].value.color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
  3529                                  case 4: m->prop[i].value.color = *((uint32_t*)data); data += 4; break;
  3530                              }
  3531                          break;
  3532  
  3533                          case m3dpf_uint8: m->prop[i].value.num = *data++; break;
  3534                          case m3dpf_uint16:m->prop[i].value.num = *((uint16_t*)data); data += 2; break;
  3535                          case m3dpf_uint32:m->prop[i].value.num = *((uint32_t*)data); data += 4; break;
  3536                          case m3dpf_float: m->prop[i].value.fnum = *((float*)data); data += 4; break;
  3537  
  3538                          case m3dpf_map:
  3539                              M3D_GETSTR(name);
  3540                              m->prop[i].value.textureid = _m3d_gettx(model, readfilecb, freecb, name);
  3541                              if(model->errcode == M3D_ERR_ALLOC) goto memerr;
  3542                              /* this error code only returned if readfilecb was specified */
  3543                              if(m->prop[i].value.textureid == M3D_UNDEF) {
  3544                                  M3D_LOG("Texture not found");
  3545                                  M3D_LOG(m->name);
  3546                                  m->numprop--;
  3547                              }
  3548                          break;
  3549  
  3550                          default:
  3551                              M3D_LOG("Unknown material property in");
  3552                              M3D_LOG(m->name);
  3553                              model->errcode = M3D_ERR_UNKPROP;
  3554                              data = chunk;
  3555                          break;
  3556                      }
  3557                  }
  3558                  m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
  3559                  if(!m->prop) goto memerr;
  3560              }
  3561          } else
  3562          /* face */
  3563          if(M3D_CHUNKMAGIC(data, 'P','R','O','C')) {
  3564              /* procedural surface */
  3565              M3D_GETSTR(name);
  3566              M3D_LOG("Procedural surface");
  3567              M3D_LOG(name);
  3568              _m3d_getpr(model, readfilecb, freecb, name);
  3569          } else
  3570          if(M3D_CHUNKMAGIC(data, 'M','E','S','H')) {
  3571              M3D_LOG("Mesh data");
  3572              if(!model->vertex) { M3D_LOG("No vertex chunk before mesh"); model->errcode = M3D_ERR_VRTS; }
  3573              /* mesh */
  3574              data += sizeof(m3dchunk_t);
  3575              mi = M3D_UNDEF;
  3576  #ifdef M3D_VERTEXMAX
  3577              pi = M3D_UNDEF;
  3578  #endif
  3579              am = model->numface;
  3580              while(data < chunk) {
  3581                  k = *data++;
  3582                  n = k >> 4;
  3583                  k &= 15;
  3584                  if(!n) {
  3585                      if(!k) {
  3586                          /* use material */
  3587                          mi = M3D_UNDEF;
  3588                          M3D_GETSTR(name);
  3589                          if(name) {
  3590                              for(j = 0; j < model->nummaterial; j++)
  3591                                  if(!strcmp(name, model->material[j].name)) {
  3592                                      mi = (M3D_INDEX)j;
  3593                                      break;
  3594                                  }
  3595                              if(mi == M3D_UNDEF) model->errcode = M3D_ERR_MTRL;
  3596                          }
  3597                      } else {
  3598                          /* use parameter */
  3599                          M3D_GETSTR(name);
  3600  #ifdef M3D_VERTEXMAX
  3601                          pi = M3D_UNDEF;
  3602                          if(name) {
  3603                              for(j = 0; j < model->numparam; j++)
  3604                                  if(!strcmp(name, model->param[j].name)) {
  3605                                      pi = (M3D_INDEX)j;
  3606                                      break;
  3607                                  }
  3608                              if(pi == M3D_UNDEF) {
  3609                                  pi = model->numparam++;
  3610                                  model->param = (m3dvi_t*)M3D_REALLOC(model->param, model->numparam * sizeof(m3dvi_t));
  3611                                  if(!model->param) goto memerr;
  3612                                  model->param[pi].name = name;
  3613                                  model->param[pi].count = 0;
  3614                              }
  3615                          }
  3616  #endif
  3617                      }
  3618                      continue;
  3619                  }
  3620                  if(n != 3) { M3D_LOG("Only triangle mesh supported for now"); model->errcode = M3D_ERR_UNKMESH; return model; }
  3621                  i = model->numface++;
  3622                  if(model->numface > am) {
  3623                      am = model->numface + 4095;
  3624                      model->face = (m3df_t*)M3D_REALLOC(model->face, am * sizeof(m3df_t));
  3625                      if(!model->face) goto memerr;
  3626                  }
  3627                  memset(&model->face[i], 255, sizeof(m3df_t)); /* set all index to -1 by default */
  3628                  model->face[i].materialid = mi;
  3629  #ifdef M3D_VERTEXMAX
  3630                  model->face[i].paramid = pi;
  3631  #endif
  3632                  for(j = 0; data < chunk && j < n; j++) {
  3633                      /* vertex */
  3634                      data = _m3d_getidx(data, model->vi_s, &model->face[i].vertex[j]);
  3635                      /* texcoord */
  3636                      if(k & 1)
  3637                          data = _m3d_getidx(data, model->ti_s, &model->face[i].texcoord[j]);
  3638                      /* normal */
  3639                      if(k & 2)
  3640                          data = _m3d_getidx(data, model->vi_s, &model->face[i].normal[j]);
  3641  #ifndef M3D_NONORMALS
  3642                      if(model->face[i].normal[j] == M3D_UNDEF) neednorm = 1;
  3643  #endif
  3644                      /* maximum */
  3645                      if(k & 4)
  3646  #ifdef M3D_VERTEXMAX
  3647                          data = _m3d_getidx(data, model->vi_s, &model->face[i].vertmax[j]);
  3648  #else
  3649                          data += model->vi_s;
  3650  #endif
  3651                  }
  3652                  if(j != n) { M3D_LOG("Invalid mesh"); model->numface = 0; model->errcode = M3D_ERR_UNKMESH; return model; }
  3653              }
  3654              model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t));
  3655          } else
  3656          if(M3D_CHUNKMAGIC(data, 'V','O','X','T')) {
  3657              /* voxel types */
  3658              M3D_LOG("Voxel types list");
  3659              if(model->voxtype) { M3D_LOG("More voxel type chunks, should be unique"); model->errcode = M3D_ERR_VOXT; continue; }
  3660              if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP;
  3661              reclen = model->ci_s + model->si_s + 3 + model->sk_s;
  3662              k = len / reclen;
  3663              model->voxtype = (m3dvt_t*)M3D_MALLOC(k * sizeof(m3dvt_t));
  3664              if(!model->voxtype) goto memerr;
  3665              memset(model->voxtype, 0, k * sizeof(m3dvt_t));
  3666              model->numvoxtype = 0;
  3667              for(i = 0, data += sizeof(m3dchunk_t); data < chunk && i < k; i++) {
  3668                  switch(model->ci_s) {
  3669                      case 1: model->voxtype[i].color = model->cmap ? model->cmap[data[0]] : 0; data++; break;
  3670                      case 2: model->voxtype[i].color = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
  3671                      case 4: model->voxtype[i].color = *((uint32_t*)data); data += 4; break;
  3672                      /* case 8: break; */
  3673                  }
  3674                  M3D_GETSTR(name);
  3675                  model->voxtype[i].materialid = M3D_UNDEF;
  3676                  if(name) {
  3677                      model->voxtype[i].name = name;
  3678  /*
  3679                      for(j = 0; j < model->nummaterial; j++)
  3680                          if(!strcmp(name, model->material[j].name)) {
  3681                              model->voxtype[i].materialid = (M3D_INDEX)j;
  3682                              break;
  3683                          }
  3684  */
  3685                  }
  3686                  j = *data++;
  3687                  model->voxtype[i].rotation = j & 0xBF;
  3688                  model->voxtype[i].voxshape = ((j & 0x40) << 2) | *data++;
  3689                  model->voxtype[i].numitem = *data++;
  3690                  model->voxtype[i].skinid = M3D_UNDEF;
  3691                  data = _m3d_getidx(data, model->sk_s, &model->voxtype[i].skinid);
  3692                  if(model->voxtype[i].numitem) {
  3693                      model->voxtype[i].item = (m3dvi_t*)M3D_MALLOC(model->voxtype[i].numitem * sizeof(m3dvi_t));
  3694                      if(!model->voxtype[i].item) goto memerr;
  3695                      memset(model->voxtype[i].item, 0, model->voxtype[i].numitem * sizeof(m3dvi_t));
  3696                      for(j = 0; j < model->voxtype[i].numitem; j++) {
  3697                          model->voxtype[i].item[j].count = *data++;
  3698                          model->voxtype[i].item[j].count |= (*data++) << 8;
  3699                          M3D_GETSTR(model->voxtype[i].item[j].name);
  3700                      }
  3701                  }
  3702              }
  3703              model->numvoxtype = i;
  3704              if(k != model->numvoxtype) {
  3705                  model->voxtype = (m3dvt_t*)M3D_REALLOC(model->voxtype, model->numvoxtype * sizeof(m3dvt_t));
  3706                  if(!model->voxtype) goto memerr;
  3707              }
  3708          } else
  3709          if(M3D_CHUNKMAGIC(data, 'V','O','X','D')) {
  3710              /* voxel data */
  3711              data += sizeof(m3dchunk_t);
  3712              M3D_GETSTR(name);
  3713              M3D_LOG("Voxel Data Layer");
  3714              M3D_LOG(name);
  3715              if(model->vd_s > 4 || model->vp_s > 2) { M3D_LOG("No voxel index size"); model->errcode = M3D_ERR_UNKVOX; continue; }
  3716              if(!model->voxtype) { M3D_LOG("No voxel type chunk before voxel data"); model->errcode = M3D_ERR_VOXT; }
  3717              i = model->numvoxel++;
  3718              model->voxel = (m3dvx_t*)M3D_REALLOC(model->voxel, model->numvoxel * sizeof(m3dvx_t));
  3719              if(!model->voxel) goto memerr;
  3720              memset(&model->voxel[i], 0, sizeof(m3dvx_t));
  3721              model->voxel[i].name = name;
  3722              switch(model->vd_s) {
  3723                  case 1:
  3724                      model->voxel[i].x = (int32_t)((int8_t)data[0]);
  3725                      model->voxel[i].y = (int32_t)((int8_t)data[1]);
  3726                      model->voxel[i].z = (int32_t)((int8_t)data[2]);
  3727                      model->voxel[i].w = (uint32_t)(data[3]);
  3728                      model->voxel[i].h = (uint32_t)(data[4]);
  3729                      model->voxel[i].d = (uint32_t)(data[5]);
  3730                      data += 6;
  3731                  break;
  3732                  case 2:
  3733                      model->voxel[i].x = (int32_t)(*((int16_t*)(data+0)));
  3734                      model->voxel[i].y = (int32_t)(*((int16_t*)(data+2)));
  3735                      model->voxel[i].z = (int32_t)(*((int16_t*)(data+4)));
  3736                      model->voxel[i].w = (uint32_t)(*((uint16_t*)(data+6)));
  3737                      model->voxel[i].h = (uint32_t)(*((uint16_t*)(data+8)));
  3738                      model->voxel[i].d = (uint32_t)(*((uint16_t*)(data+10)));
  3739                      data += 12;
  3740                  break;
  3741                  case 4:
  3742                      model->voxel[i].x = *((int32_t*)(data+0));
  3743                      model->voxel[i].y = *((int32_t*)(data+4));
  3744                      model->voxel[i].z = *((int32_t*)(data+8));
  3745                      model->voxel[i].w = *((uint32_t*)(data+12));
  3746                      model->voxel[i].h = *((uint32_t*)(data+16));
  3747                      model->voxel[i].d = *((uint32_t*)(data+20));
  3748                      data += 24;
  3749                  break;
  3750              }
  3751              model->voxel[i].uncertain = *data++;
  3752              model->voxel[i].groupid = *data++;
  3753              k = model->voxel[i].w * model->voxel[i].h * model->voxel[i].d;
  3754              model->voxel[i].data = (M3D_VOXEL*)M3D_MALLOC(k * sizeof(M3D_VOXEL));
  3755              if(!model->voxel[i].data) goto memerr;
  3756              memset(model->voxel[i].data, 0xff, k * sizeof(M3D_VOXEL));
  3757              for(j = 0; data < chunk && j < k;) {
  3758                  l = ((*data++) & 0x7F) + 1;
  3759                  if(data[-1] & 0x80) {
  3760                      data = _m3d_getidx(data, model->vp_s, &mi);
  3761                      while(l-- && j < k) model->voxel[i].data[j++] = (M3D_VOXEL)mi;
  3762                  } else
  3763                      while(l-- && j < k) {
  3764                          data = _m3d_getidx(data, model->vp_s, &mi);
  3765                          model->voxel[i].data[j++] = (M3D_VOXEL)mi;
  3766                      }
  3767              }
  3768          } else
  3769          if(M3D_CHUNKMAGIC(data, 'S','H','P','E')) {
  3770              /* mathematical shape */
  3771              data += sizeof(m3dchunk_t);
  3772              M3D_GETSTR(name);
  3773              M3D_LOG("Mathematical Shape");
  3774              M3D_LOG(name);
  3775              i = model->numshape++;
  3776              model->shape = (m3dh_t*)M3D_REALLOC(model->shape, model->numshape * sizeof(m3dh_t));
  3777              if(!model->shape) goto memerr;
  3778              h = &model->shape[i];
  3779              h->numcmd = 0;
  3780              h->cmd = NULL;
  3781              h->name = name;
  3782              h->group = M3D_UNDEF;
  3783              data = _m3d_getidx(data, model->bi_s, &h->group);
  3784              if(h->group != M3D_UNDEF && h->group >= model->numbone) {
  3785                  M3D_LOG("Unknown bone id as shape group in shape");
  3786                  M3D_LOG(name);
  3787                  h->group = M3D_UNDEF;
  3788                  model->errcode = M3D_ERR_SHPE;
  3789              }
  3790              while(data < chunk) {
  3791                  i = h->numcmd++;
  3792                  h->cmd = (m3dc_t*)M3D_REALLOC(h->cmd, h->numcmd * sizeof(m3dc_t));
  3793                  if(!h->cmd) goto memerr;
  3794                  h->cmd[i].type = *data++;
  3795                  if(h->cmd[i].type & 0x80) {
  3796                      h->cmd[i].type &= 0x7F;
  3797                      h->cmd[i].type |= (*data++ << 7);
  3798                  }
  3799                  if(h->cmd[i].type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0]))) {
  3800                      M3D_LOG("Unknown shape command in");
  3801                      M3D_LOG(h->name);
  3802                      model->errcode = M3D_ERR_UNKCMD;
  3803                      break;
  3804                  }
  3805                  cd = &m3d_commandtypes[h->cmd[i].type];
  3806                  h->cmd[i].arg = (uint32_t*)M3D_MALLOC(cd->p * sizeof(uint32_t));
  3807                  if(!h->cmd[i].arg) goto memerr;
  3808                  memset(h->cmd[i].arg, 0, cd->p * sizeof(uint32_t));
  3809                  for(k = n = 0, l = cd->p; k < l; k++)
  3810                      switch(cd->a[((k - n) % (cd->p - n)) + n]) {
  3811                          case m3dcp_mi_t:
  3812                              h->cmd[i].arg[k] = M3D_NOTDEFINED;
  3813                              M3D_GETSTR(name);
  3814                              if(name) {
  3815                                  for(n = 0; n < model->nummaterial; n++)
  3816                                      if(!strcmp(name, model->material[n].name)) {
  3817                                          h->cmd[i].arg[k] = n;
  3818                                          break;
  3819                                      }
  3820                                  if(h->cmd[i].arg[k] == M3D_NOTDEFINED) model->errcode = M3D_ERR_MTRL;
  3821                              }
  3822                          break;
  3823                          case m3dcp_vc_t:
  3824                              f = 0.0f;
  3825                              switch(model->vc_s) {
  3826                                  case 1: f = (float)((int8_t)data[0]) / 127; break;
  3827                                  case 2: f = (float)(*((int16_t*)(data+0))) / 32767; break;
  3828                                  case 4: f = (float)(*((float*)(data+0))); break;
  3829                                  case 8: f = (float)(*((double*)(data+0))); break;
  3830                              }
  3831                              memcpy(&h->cmd[i].arg[k], &f, 4);
  3832                              data += model->vc_s;
  3833                          break;
  3834                          case m3dcp_hi_t: data = _m3d_getidx(data, model->hi_s, &h->cmd[i].arg[k]); break;
  3835                          case m3dcp_fi_t: data = _m3d_getidx(data, model->fi_s, &h->cmd[i].arg[k]); break;
  3836                          case m3dcp_ti_t: data = _m3d_getidx(data, model->ti_s, &h->cmd[i].arg[k]); break;
  3837                          case m3dcp_qi_t:
  3838                          case m3dcp_vi_t: data = _m3d_getidx(data, model->vi_s, &h->cmd[i].arg[k]); break;
  3839                          case m3dcp_i1_t: data = _m3d_getidx(data, 1, &h->cmd[i].arg[k]); break;
  3840                          case m3dcp_i2_t: data = _m3d_getidx(data, 2, &h->cmd[i].arg[k]); break;
  3841                          case m3dcp_i4_t: data = _m3d_getidx(data, 4, &h->cmd[i].arg[k]); break;
  3842                          case m3dcp_va_t: data = _m3d_getidx(data, 4, &h->cmd[i].arg[k]);
  3843                              n = k + 1; l += (h->cmd[i].arg[k] - 1) * (cd->p - k - 1);
  3844                              h->cmd[i].arg = (uint32_t*)M3D_REALLOC(h->cmd[i].arg, l * sizeof(uint32_t));
  3845                              if(!h->cmd[i].arg) goto memerr;
  3846                              memset(&h->cmd[i].arg[k + 1], 0, (l - k - 1) * sizeof(uint32_t));
  3847                          break;
  3848                      }
  3849              }
  3850          } else
  3851          /* annotation label list */
  3852          if(M3D_CHUNKMAGIC(data, 'L','B','L','S')) {
  3853              data += sizeof(m3dchunk_t);
  3854              M3D_GETSTR(name);
  3855              M3D_GETSTR(lang);
  3856              M3D_LOG("Label list");
  3857              if(name) { M3D_LOG(name); }
  3858              if(lang) { M3D_LOG(lang); }
  3859              if(model->ci_s && model->ci_s < 4 && !model->cmap) model->errcode = M3D_ERR_CMAP;
  3860              k = 0;
  3861              switch(model->ci_s) {
  3862                  case 1: k = model->cmap ? model->cmap[data[0]] : 0; data++; break;
  3863                  case 2: k = model->cmap ? model->cmap[*((uint16_t*)data)] : 0; data += 2; break;
  3864                  case 4: k = *((uint32_t*)data); data += 4; break;
  3865                  /* case 8: break; */
  3866              }
  3867              reclen = model->vi_s + model->si_s;
  3868              i = model->numlabel; model->numlabel += len / reclen;
  3869              model->label = (m3dl_t*)M3D_REALLOC(model->label, model->numlabel * sizeof(m3dl_t));
  3870              if(!model->label) goto memerr;
  3871              memset(&model->label[i], 0, (model->numlabel - i) * sizeof(m3dl_t));
  3872              for(; data < chunk && i < model->numlabel; i++) {
  3873                  model->label[i].name = name;
  3874                  model->label[i].lang = lang;
  3875                  model->label[i].color = k;
  3876                  data = _m3d_getidx(data, model->vi_s, &model->label[i].vertexid);
  3877                  M3D_GETSTR(model->label[i].text);
  3878              }
  3879          } else
  3880          /* action */
  3881          if(M3D_CHUNKMAGIC(data, 'A','C','T','N')) {
  3882              M3D_LOG("Action");
  3883              i = model->numaction++;
  3884              model->action = (m3da_t*)M3D_REALLOC(model->action, model->numaction * sizeof(m3da_t));
  3885              if(!model->action) goto memerr;
  3886              a = &model->action[i];
  3887              data += sizeof(m3dchunk_t);
  3888              M3D_GETSTR(a->name);
  3889              M3D_LOG(a->name);
  3890              a->numframe = *((uint16_t*)data); data += 2;
  3891              if(a->numframe < 1) {
  3892                  model->numaction--;
  3893              } else {
  3894                  a->durationmsec = *((uint32_t*)data); data += 4;
  3895                  a->frame = (m3dfr_t*)M3D_MALLOC(a->numframe * sizeof(m3dfr_t));
  3896                  if(!a->frame) goto memerr;
  3897                  for(i = 0; data < chunk && i < a->numframe; i++) {
  3898                      a->frame[i].msec = *((uint32_t*)data); data += 4;
  3899                      a->frame[i].numtransform = 0; a->frame[i].transform = NULL;
  3900                      data = _m3d_getidx(data, model->fc_s, &a->frame[i].numtransform);
  3901                      if(a->frame[i].numtransform > 0) {
  3902                          a->frame[i].transform = (m3dtr_t*)M3D_MALLOC(a->frame[i].numtransform * sizeof(m3dtr_t));
  3903                          for(j = 0; j < a->frame[i].numtransform; j++) {
  3904                              data = _m3d_getidx(data, model->bi_s, &a->frame[i].transform[j].boneid);
  3905                              data = _m3d_getidx(data, model->vi_s, &a->frame[i].transform[j].pos);
  3906                              data = _m3d_getidx(data, model->vi_s, &a->frame[i].transform[j].ori);
  3907                          }
  3908                      }
  3909                  }
  3910              }
  3911          } else {
  3912              i = model->numextra++;
  3913              model->extra = (m3dchunk_t**)M3D_REALLOC(model->extra, model->numextra * sizeof(m3dchunk_t*));
  3914              if(!model->extra) goto memerr;
  3915              model->extra[i] = (m3dchunk_t*)data;
  3916          }
  3917      }
  3918      /* calculate normals, normalize skin weights, create bone/vertex cross-references and calculate transform matrices */
  3919  #ifdef M3D_ASCII
  3920  postprocess:
  3921  #endif
  3922      if(model) {
  3923          M3D_LOG("Post-process");
  3924  #ifdef M3D_PROFILING
  3925          gettimeofday(&tv1, NULL);
  3926          tvd.tv_sec = tv1.tv_sec - tv0.tv_sec;
  3927          tvd.tv_usec = tv1.tv_usec - tv0.tv_usec;
  3928          if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
  3929          printf("  Parsing chunks  %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec);
  3930  #endif
  3931  #ifndef M3D_NOVOXELS
  3932          if(model->numvoxel && model->voxel) {
  3933              M3D_LOG("Converting voxels into vertices and mesh");
  3934              /* add normals */
  3935              enorm = model->numvertex; model->numvertex += 6;
  3936              model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
  3937              if(!model->vertex) goto memerr;
  3938              memset(&model->vertex[enorm], 0, 6 * sizeof(m3dv_t));
  3939              for(l = 0; l < 6; l++)
  3940                  model->vertex[enorm+l].skinid = M3D_UNDEF;
  3941              model->vertex[enorm+0].y = (M3D_FLOAT)-1.0;
  3942              model->vertex[enorm+1].z = (M3D_FLOAT)-1.0;
  3943              model->vertex[enorm+2].x = (M3D_FLOAT)-1.0;
  3944              model->vertex[enorm+3].y = (M3D_FLOAT)1.0;
  3945              model->vertex[enorm+4].z = (M3D_FLOAT)1.0;
  3946              model->vertex[enorm+5].x = (M3D_FLOAT)1.0;
  3947              /* this is a fast, not so memory efficient version, only basic face culling used */
  3948              min_x = min_y = min_z = 2147483647L;
  3949              max_x = max_y = max_z = -2147483647L;
  3950              for(i = 0; i < model->numvoxel; i++) {
  3951                  if(model->voxel[i].x + (int32_t)model->voxel[i].w > max_x) max_x = model->voxel[i].x + (int32_t)model->voxel[i].w;
  3952                  if(model->voxel[i].x < min_x) min_x = model->voxel[i].x;
  3953                  if(model->voxel[i].y + (int32_t)model->voxel[i].h > max_y) max_y = model->voxel[i].y + (int32_t)model->voxel[i].h;
  3954                  if(model->voxel[i].y < min_y) min_y = model->voxel[i].y;
  3955                  if(model->voxel[i].z + (int32_t)model->voxel[i].d > max_z) max_z = model->voxel[i].z + (int32_t)model->voxel[i].d;
  3956                  if(model->voxel[i].z < min_z) min_z = model->voxel[i].z;
  3957              }
  3958              i = (-min_x > max_x ? -min_x : max_x);
  3959              j = (-min_y > max_y ? -min_y : max_y);
  3960              k = (-min_z > max_z ? -min_z : max_z);
  3961              if(j > i) i = j;
  3962              if(k > i) i = k;
  3963              if(i <= 1) i = 1;
  3964              w = (M3D_FLOAT)1.0 / (M3D_FLOAT)i;
  3965              if(i >= 254) model->vc_s = 2;
  3966              if(i >= 65534) model->vc_s = 4;
  3967              for(i = 0; i < model->numvoxel; i++) {
  3968                  sx = model->voxel[i].w; sz = model->voxel[i].d; sy = model->voxel[i].h;
  3969                  for(y = 0, j = 0; y < sy; y++)
  3970                      for(z = 0; z < sz; z++)
  3971                          for(x = 0; x < sx; x++, j++)
  3972                              if(model->voxel[i].data[j] < model->numvoxtype) {
  3973                                  k = 0;
  3974                                  /*  16__32     ____
  3975                                   *  /|  /|    /|2 /|
  3976                                   *64_128 |   /_8_/ 32
  3977                                   * | 1_|_2   |4|_|_|
  3978                                   * |/  |/    |/ 1|/
  3979                                   * 4___8     |16_|    */
  3980                                  k = n = am = 0;
  3981                                  if(!y || model->voxel[i].data[j - sx*sz] >= model->numvoxtype) { n++; am |= 1; k |= 1|2|4|8; }
  3982                                  if(!z || model->voxel[i].data[j - sx] >= model->numvoxtype) { n++; am |= 2; k |= 1|2|16|32; }
  3983                                  if(!x || model->voxel[i].data[j - 1] >= model->numvoxtype) { n++; am |= 4; k |= 1|4|16|64; }
  3984                                  if(y == sy-1 || model->voxel[i].data[j + sx*sz] >= model->numvoxtype) { n++; am |= 8; k |= 16|32|64|128; }
  3985                                  if(z == sz-1 || model->voxel[i].data[j + sx] >= model->numvoxtype) { n++; am |= 16; k |= 4|8|64|128; }
  3986                                  if(x == sx-1 || model->voxel[i].data[j + 1] >= model->numvoxtype) { n++; am |= 32; k |= 2|8|32|128; }
  3987                                  if(k) {
  3988                                      memset(edge, 255, sizeof(edge));
  3989                                      for(l = 0, len = 1, reclen = model->numvertex; l < 8; l++, len <<= 1)
  3990                                          if(k & len) edge[l] = model->numvertex++;
  3991                                      model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
  3992                                      if(!model->vertex) goto memerr;
  3993                                      memset(&model->vertex[reclen], 0, (model->numvertex-reclen) * sizeof(m3dv_t));
  3994                                      for(l = reclen; l < model->numvertex; l++) {
  3995                                          model->vertex[l].skinid = model->voxtype[model->voxel[i].data[j]].skinid;
  3996                                          model->vertex[l].color = model->voxtype[model->voxel[i].data[j]].color;
  3997                                      }
  3998                                      l = reclen;
  3999                                      if(k & 1) {
  4000                                          model->vertex[l].x = (model->voxel[i].x + x) * w;
  4001                                          model->vertex[l].y = (model->voxel[i].y + y) * w;
  4002                                          model->vertex[l].z = (model->voxel[i].z + z) * w;
  4003                                          l++;
  4004                                      }
  4005                                      if(k & 2) {
  4006                                          model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
  4007                                          model->vertex[l].y = (model->voxel[i].y + y) * w;
  4008                                          model->vertex[l].z = (model->voxel[i].z + z) * w;
  4009                                          l++;
  4010                                      }
  4011                                      if(k & 4) {
  4012                                          model->vertex[l].x = (model->voxel[i].x + x) * w;
  4013                                          model->vertex[l].y = (model->voxel[i].y + y) * w;
  4014                                          model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
  4015                                          l++;
  4016                                      }
  4017                                      if(k & 8) {
  4018                                          model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
  4019                                          model->vertex[l].y = (model->voxel[i].y + y) * w;
  4020                                          model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
  4021                                          l++;
  4022                                      }
  4023                                      if(k & 16) {
  4024                                          model->vertex[l].x = (model->voxel[i].x + x) * w;
  4025                                          model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
  4026                                          model->vertex[l].z = (model->voxel[i].z + z) * w;
  4027                                          l++;
  4028                                      }
  4029                                      if(k & 32) {
  4030                                          model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
  4031                                          model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
  4032                                          model->vertex[l].z = (model->voxel[i].z + z) * w;
  4033                                          l++;
  4034                                      }
  4035                                      if(k & 64) {
  4036                                          model->vertex[l].x = (model->voxel[i].x + x) * w;
  4037                                          model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
  4038                                          model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
  4039                                          l++;
  4040                                      }
  4041                                      if(k & 128) {
  4042                                          model->vertex[l].x = (model->voxel[i].x + x + 1) * w;
  4043                                          model->vertex[l].y = (model->voxel[i].y + y + 1) * w;
  4044                                          model->vertex[l].z = (model->voxel[i].z + z + 1) * w;
  4045                                          l++;
  4046                                      }
  4047                                      n <<= 1;
  4048                                      l = model->numface; model->numface += n;
  4049                                      model->face = (m3df_t*)M3D_REALLOC(model->face, model->numface * sizeof(m3df_t));
  4050                                      if(!model->face) goto memerr;
  4051                                      memset(&model->face[l], 255, n * sizeof(m3df_t));
  4052                                      for(reclen = l; reclen < model->numface; reclen++)
  4053                                          model->face[reclen].materialid = model->voxtype[model->voxel[i].data[j]].materialid;
  4054                                      if(am & 1) {            /* bottom */
  4055                                          model->face[l].vertex[0] = edge[0];   model->face[l].vertex[1] = edge[1];   model->face[l].vertex[2] = edge[2];
  4056                                          model->face[l+1].vertex[0] = edge[2]; model->face[l+1].vertex[1] = edge[1]; model->face[l+1].vertex[2] = edge[3];
  4057                                          model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
  4058                                          model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm;
  4059                                          l += 2;
  4060                                      }
  4061                                      if(am & 2) {            /* north */
  4062                                          model->face[l].vertex[0] = edge[0];   model->face[l].vertex[1] = edge[4];   model->face[l].vertex[2] = edge[1];
  4063                                          model->face[l+1].vertex[0] = edge[1]; model->face[l+1].vertex[1] = edge[4]; model->face[l+1].vertex[2] = edge[5];
  4064                                          model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
  4065                                          model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+1;
  4066                                          l += 2;
  4067                                      }
  4068                                      if(am & 4) {            /* west */
  4069                                          model->face[l].vertex[0] = edge[0];   model->face[l].vertex[1] = edge[2];   model->face[l].vertex[2] = edge[4];
  4070                                          model->face[l+1].vertex[0] = edge[2]; model->face[l+1].vertex[1] = edge[6]; model->face[l+1].vertex[2] = edge[4];
  4071                                          model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
  4072                                          model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+2;
  4073                                          l += 2;
  4074                                      }
  4075                                      if(am & 8) {            /* top */
  4076                                          model->face[l].vertex[0] = edge[4];   model->face[l].vertex[1] = edge[6];   model->face[l].vertex[2] = edge[5];
  4077                                          model->face[l+1].vertex[0] = edge[5]; model->face[l+1].vertex[1] = edge[6]; model->face[l+1].vertex[2] = edge[7];
  4078                                          model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
  4079                                          model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+3;
  4080                                          l += 2;
  4081                                      }
  4082                                      if(am & 16) {           /* south */
  4083                                          model->face[l].vertex[0] = edge[2];   model->face[l].vertex[1] = edge[7];   model->face[l].vertex[2] = edge[6];
  4084                                          model->face[l+1].vertex[0] = edge[7]; model->face[l+1].vertex[1] = edge[2]; model->face[l+1].vertex[2] = edge[3];
  4085                                          model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
  4086                                          model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+4;
  4087                                          l += 2;
  4088                                      }
  4089                                      if(am & 32) {           /* east */
  4090                                          model->face[l].vertex[0] = edge[1];   model->face[l].vertex[1] = edge[5];   model->face[l].vertex[2] = edge[7];
  4091                                          model->face[l+1].vertex[0] = edge[1]; model->face[l+1].vertex[1] = edge[7]; model->face[l+1].vertex[2] = edge[3];
  4092                                          model->face[l].normal[0] = model->face[l].normal[1] = model->face[l].normal[2] =
  4093                                          model->face[l+1].normal[0] = model->face[l+1].normal[1] = model->face[l+1].normal[2] = enorm+5;
  4094                                          l += 2;
  4095                                      }
  4096                                  }
  4097                              }
  4098              }
  4099          }
  4100  #endif
  4101  #ifndef M3D_NONORMALS
  4102          if(model->numface && model->face && neednorm) {
  4103              /* if they are missing, calculate triangle normals into a temporary buffer */
  4104              norm = (m3dv_t*)M3D_MALLOC(model->numface * sizeof(m3dv_t));
  4105              if(!norm) goto memerr;
  4106              for(i = 0, n = model->numvertex; i < model->numface; i++)
  4107                  if(model->face[i].normal[0] == M3D_UNDEF) {
  4108                      v0 = &model->vertex[model->face[i].vertex[0]];
  4109                      v1 = &model->vertex[model->face[i].vertex[1]];
  4110                      v2 = &model->vertex[model->face[i].vertex[2]];
  4111                      va.x = v1->x - v0->x; va.y = v1->y - v0->y; va.z = v1->z - v0->z;
  4112                      vb.x = v2->x - v0->x; vb.y = v2->y - v0->y; vb.z = v2->z - v0->z;
  4113                      v0 = &norm[i];
  4114                      v0->x = (va.y * vb.z) - (va.z * vb.y);
  4115                      v0->y = (va.z * vb.x) - (va.x * vb.z);
  4116                      v0->z = (va.x * vb.y) - (va.y * vb.x);
  4117                      w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z));
  4118                      v0->x *= w; v0->y *= w; v0->z *= w;
  4119                      model->face[i].normal[0] = model->face[i].vertex[0] + n;
  4120                      model->face[i].normal[1] = model->face[i].vertex[1] + n;
  4121                      model->face[i].normal[2] = model->face[i].vertex[2] + n;
  4122                  }
  4123              /* this is the fast way, we don't care if a normal is repeated in model->vertex */
  4124              M3D_LOG("Generating normals");
  4125              model->flags |= M3D_FLG_GENNORM;
  4126              model->numvertex <<= 1;
  4127              model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, model->numvertex * sizeof(m3dv_t));
  4128              if(!model->vertex) goto memerr;
  4129              memset(&model->vertex[n], 0, n * sizeof(m3dv_t));
  4130              for(i = 0; i < model->numface; i++)
  4131                  for(j = 0; j < 3; j++) {
  4132                      v0 = &model->vertex[model->face[i].vertex[j] + n];
  4133                      v0->x += norm[i].x;
  4134                      v0->y += norm[i].y;
  4135                      v0->z += norm[i].z;
  4136                  }
  4137              /* for each vertex, take the average of the temporary normals and use that */
  4138              for(i = 0, v0 = &model->vertex[n]; i < n; i++, v0++) {
  4139                  w = _m3d_rsq((v0->x * v0->x) + (v0->y * v0->y) + (v0->z * v0->z));
  4140                  v0->x *= w; v0->y *= w; v0->z *= w;
  4141                  v0->skinid = M3D_UNDEF;
  4142              }
  4143              M3D_FREE(norm);
  4144          }
  4145  #endif
  4146          if(model->numbone && model->bone && model->numskin && model->skin && model->numvertex && model->vertex) {
  4147  #ifndef M3D_NOWEIGHTS
  4148              M3D_LOG("Generating weight cross-reference");
  4149              for(i = 0; i < model->numvertex; i++) {
  4150                  if(model->vertex[i].skinid < model->numskin) {
  4151                      sk = &model->skin[model->vertex[i].skinid];
  4152                      w = (M3D_FLOAT)0.0;
  4153                      for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++)
  4154                          w += sk->weight[j];
  4155                      for(j = 0; j < M3D_NUMBONE && sk->boneid[j] != M3D_UNDEF && sk->weight[j] > (M3D_FLOAT)0.0; j++) {
  4156                          sk->weight[j] /= w;
  4157                          b = &model->bone[sk->boneid[j]];
  4158                          k = b->numweight++;
  4159                          b->weight = (m3dw_t*)M3D_REALLOC(b->weight, b->numweight * sizeof(m3da_t));
  4160                          if(!b->weight) goto memerr;
  4161                          b->weight[k].vertexid = i;
  4162                          b->weight[k].weight = sk->weight[j];
  4163                      }
  4164                  }
  4165              }
  4166  #endif
  4167  #ifndef M3D_NOANIMATION
  4168              M3D_LOG("Calculating bone transformation matrices");
  4169              for(i = 0; i < model->numbone; i++) {
  4170                  b = &model->bone[i];
  4171                  if(model->bone[i].parent == M3D_UNDEF) {
  4172                      _m3d_mat((M3D_FLOAT*)&b->mat4, &model->vertex[b->pos], &model->vertex[b->ori]);
  4173                  } else {
  4174                      _m3d_mat((M3D_FLOAT*)&r, &model->vertex[b->pos], &model->vertex[b->ori]);
  4175                      _m3d_mul((M3D_FLOAT*)&b->mat4, (M3D_FLOAT*)&model->bone[b->parent].mat4, (M3D_FLOAT*)&r);
  4176                  }
  4177              }
  4178              for(i = 0; i < model->numbone; i++)
  4179                  _m3d_inv((M3D_FLOAT*)&model->bone[i].mat4);
  4180  #endif
  4181          }
  4182  #ifdef M3D_PROFILING
  4183          gettimeofday(&tv0, NULL);
  4184          tvd.tv_sec = tv0.tv_sec - tv1.tv_sec;
  4185          tvd.tv_usec = tv0.tv_usec - tv1.tv_usec;
  4186          if(tvd.tv_usec < 0) { tvd.tv_sec--; tvd.tv_usec += 1000000L; }
  4187          printf("  Post-process    %ld.%06ld sec\n", tvd.tv_sec, tvd.tv_usec);
  4188  #endif
  4189      }
  4190      return model;
  4191  }
  4192  
  4193  /**
  4194   * Calculates skeletons for animation frames, returns a working copy (should be freed after use)
  4195   */
  4196  m3dtr_t *m3d_frame(m3d_t *model, M3D_INDEX actionid, M3D_INDEX frameid, m3dtr_t *skeleton)
  4197  {
  4198      unsigned int i;
  4199      M3D_INDEX s = frameid;
  4200      m3dfr_t *fr;
  4201  
  4202      if(!model || !model->numbone || !model->bone || (actionid != M3D_UNDEF && (!model->action ||
  4203          actionid >= model->numaction || frameid >= model->action[actionid].numframe))) {
  4204              model->errcode = M3D_ERR_UNKFRAME;
  4205              return skeleton;
  4206      }
  4207      model->errcode = M3D_SUCCESS;
  4208      if(!skeleton) {
  4209          skeleton = (m3dtr_t*)M3D_MALLOC(model->numbone * sizeof(m3dtr_t));
  4210          if(!skeleton) {
  4211              model->errcode = M3D_ERR_ALLOC;
  4212              return NULL;
  4213          }
  4214          goto gen;
  4215      }
  4216      if(actionid == M3D_UNDEF || !frameid) {
  4217  gen:    s = 0;
  4218          for(i = 0; i < model->numbone; i++) {
  4219              skeleton[i].boneid = i;
  4220              skeleton[i].pos = model->bone[i].pos;
  4221              skeleton[i].ori = model->bone[i].ori;
  4222          }
  4223      }
  4224      if(actionid < model->numaction && (frameid || !model->action[actionid].frame[0].msec)) {
  4225          for(; s <= frameid; s++) {
  4226              fr = &model->action[actionid].frame[s];
  4227              for(i = 0; i < fr->numtransform; i++) {
  4228                  skeleton[fr->transform[i].boneid].pos = fr->transform[i].pos;
  4229                  skeleton[fr->transform[i].boneid].ori = fr->transform[i].ori;
  4230              }
  4231          }
  4232      }
  4233      return skeleton;
  4234  }
  4235  
  4236  #ifndef M3D_NOANIMATION
  4237  /**
  4238   * Returns interpolated animation-pose, a working copy (should be freed after use)
  4239   */
  4240  m3db_t *m3d_pose(m3d_t *model, M3D_INDEX actionid, uint32_t msec)
  4241  {
  4242      unsigned int i, j, l;
  4243      M3D_FLOAT r[16], t, c, d, s;
  4244      m3db_t *ret;
  4245      m3dv_t *v, *p, *f;
  4246      m3dtr_t *tmp;
  4247      m3dfr_t *fr;
  4248  
  4249      if(!model || !model->numbone || !model->bone) {
  4250          model->errcode = M3D_ERR_UNKFRAME;
  4251          return NULL;
  4252      }
  4253      ret = (m3db_t*)M3D_MALLOC(model->numbone * sizeof(m3db_t));
  4254      if(!ret) {
  4255          model->errcode = M3D_ERR_ALLOC;
  4256          return NULL;
  4257      }
  4258      memcpy(ret, model->bone, model->numbone * sizeof(m3db_t));
  4259      for(i = 0; i < model->numbone; i++)
  4260          _m3d_inv((M3D_FLOAT*)&ret[i].mat4);
  4261      if(!model->action || actionid >= model->numaction) {
  4262          model->errcode = M3D_ERR_UNKFRAME;
  4263          return ret;
  4264      }
  4265      msec %= model->action[actionid].durationmsec;
  4266      model->errcode = M3D_SUCCESS;
  4267      fr = &model->action[actionid].frame[0];
  4268      for(j = l = 0; j < model->action[actionid].numframe && model->action[actionid].frame[j].msec <= msec; j++) {
  4269          fr = &model->action[actionid].frame[j];
  4270          l = fr->msec;
  4271          for(i = 0; i < fr->numtransform; i++) {
  4272              ret[fr->transform[i].boneid].pos = fr->transform[i].pos;
  4273              ret[fr->transform[i].boneid].ori = fr->transform[i].ori;
  4274          }
  4275      }
  4276      if(l != msec) {
  4277          model->vertex = (m3dv_t*)M3D_REALLOC(model->vertex, (model->numvertex + 2 * model->numbone) * sizeof(m3dv_t));
  4278          if(!model->vertex) {
  4279              free(ret);
  4280              model->errcode = M3D_ERR_ALLOC;
  4281              return NULL;
  4282          }
  4283          tmp = (m3dtr_t*)M3D_MALLOC(model->numbone * sizeof(m3dtr_t));
  4284          if(tmp) {
  4285              for(i = 0; i < model->numbone; i++) {
  4286                  tmp[i].pos = ret[i].pos;
  4287                  tmp[i].ori = ret[i].ori;
  4288              }
  4289              fr = &model->action[actionid].frame[j % model->action[actionid].numframe];
  4290              t = l >= fr->msec ? (M3D_FLOAT)1.0 : (M3D_FLOAT)(msec - l) / (M3D_FLOAT)(fr->msec - l);
  4291              for(i = 0; i < fr->numtransform; i++) {
  4292                  tmp[fr->transform[i].boneid].pos = fr->transform[i].pos;
  4293                  tmp[fr->transform[i].boneid].ori = fr->transform[i].ori;
  4294              }
  4295              for(i = 0, j = model->numvertex; i < model->numbone; i++) {
  4296                  /* interpolation of position */
  4297                  if(ret[i].pos != tmp[i].pos) {
  4298                      p = &model->vertex[ret[i].pos];
  4299                      f = &model->vertex[tmp[i].pos];
  4300                      v = &model->vertex[j];
  4301                      v->x = p->x + t * (f->x - p->x);
  4302                      v->y = p->y + t * (f->y - p->y);
  4303                      v->z = p->z + t * (f->z - p->z);
  4304                      ret[i].pos = j++;
  4305                  }
  4306                  /* interpolation of orientation */
  4307                  if(ret[i].ori != tmp[i].ori) {
  4308                      p = &model->vertex[ret[i].ori];
  4309                      f = &model->vertex[tmp[i].ori];
  4310                      v = &model->vertex[j];
  4311                      d = p->w * f->w + p->x * f->x + p->y * f->y + p->z * f->z;
  4312                      if(d < 0) { d = -d; s = (M3D_FLOAT)-1.0; } else s = (M3D_FLOAT)1.0;
  4313  #if 0
  4314                      /* don't use SLERP, requires two more variables, libm linkage and it is slow (but nice) */
  4315                      a = (M3D_FLOAT)1.0 - t; b = t;
  4316                      if(d < (M3D_FLOAT)0.999999) { c = acosf(d); b = 1 / sinf(c); a = sinf(a * c) * b; b *= sinf(t * c) * s; }
  4317                      v->x = p->x * a + f->x * b;
  4318                      v->y = p->y * a + f->y * b;
  4319                      v->z = p->z * a + f->z * b;
  4320                      v->w = p->w * a + f->w * b;
  4321  #else
  4322                      /* approximated NLERP, original approximation by Arseny Kapoulkine, heavily optimized by me */
  4323                      c = t - (M3D_FLOAT)0.5; t += t * c * (t - (M3D_FLOAT)1.0) * (((M3D_FLOAT)1.0904 + d * ((M3D_FLOAT)-3.2452 +
  4324                          d * ((M3D_FLOAT)3.55645 - d * (M3D_FLOAT)1.43519))) * c * c + ((M3D_FLOAT)0.848013 + d *
  4325                          ((M3D_FLOAT)-1.06021 + d * (M3D_FLOAT)0.215638)));
  4326                      v->x = p->x + t * (s * f->x - p->x);
  4327                      v->y = p->y + t * (s * f->y - p->y);
  4328                      v->z = p->z + t * (s * f->z - p->z);
  4329                      v->w = p->w + t * (s * f->w - p->w);
  4330                      d = _m3d_rsq(v->w * v->w + v->x * v->x + v->y * v->y + v->z * v->z);
  4331                      v->x *= d; v->y *= d; v->z *= d; v->w *= d;
  4332  #endif
  4333                      ret[i].ori = j++;
  4334                  }
  4335              }
  4336              M3D_FREE(tmp);
  4337          }
  4338      }
  4339      for(i = 0; i < model->numbone; i++) {
  4340          if(ret[i].parent == M3D_UNDEF) {
  4341              _m3d_mat((M3D_FLOAT*)&ret[i].mat4, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]);
  4342          } else {
  4343              _m3d_mat((M3D_FLOAT*)&r, &model->vertex[ret[i].pos], &model->vertex[ret[i].ori]);
  4344              _m3d_mul((M3D_FLOAT*)&ret[i].mat4, (M3D_FLOAT*)&ret[ret[i].parent].mat4, (M3D_FLOAT*)&r);
  4345          }
  4346      }
  4347      return ret;
  4348  }
  4349  
  4350  #endif /* M3D_NOANIMATION */
  4351  
  4352  #endif /* M3D_IMPLEMENTATION */
  4353  
  4354  #if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_EXPORTER))
  4355  /**
  4356   * Free the in-memory model
  4357   */
  4358  void m3d_free(m3d_t *model)
  4359  {
  4360      unsigned int i, j;
  4361  
  4362      if(!model) return;
  4363  #ifdef M3D_ASCII
  4364      /* if model imported from ASCII, we have to free all strings as well */
  4365      if(model->flags & M3D_FLG_FREESTR) {
  4366          if(model->name) M3D_FREE(model->name);
  4367          if(model->license) M3D_FREE(model->license);
  4368          if(model->author) M3D_FREE(model->author);
  4369          if(model->desc) M3D_FREE(model->desc);
  4370          if(model->bone)
  4371              for(i = 0; i < model->numbone; i++)
  4372                  if(model->bone[i].name)
  4373                      M3D_FREE(model->bone[i].name);
  4374          if(model->shape)
  4375              for(i = 0; i < model->numshape; i++)
  4376                  if(model->shape[i].name)
  4377                      M3D_FREE(model->shape[i].name);
  4378          if(model->numvoxtype)
  4379              for(i = 0; i < model->numvoxtype; i++) {
  4380                  if(model->voxtype[i].name)
  4381                      M3D_FREE(model->voxtype[i].name);
  4382                  for(j = 0; j < model->voxtype[i].numitem; j++)
  4383                      if(model->voxtype[i].item[j].name)
  4384                          M3D_FREE(model->voxtype[i].item[j].name);
  4385              }
  4386          if(model->numvoxel)
  4387              for(i = 0; i < model->numvoxel; i++)
  4388                  if(model->voxel[i].name)
  4389                      M3D_FREE(model->voxel[i].name);
  4390          if(model->material)
  4391              for(i = 0; i < model->nummaterial; i++)
  4392                  if(model->material[i].name)
  4393                      M3D_FREE(model->material[i].name);
  4394          if(model->action)
  4395              for(i = 0; i < model->numaction; i++)
  4396                  if(model->action[i].name)
  4397                      M3D_FREE(model->action[i].name);
  4398          if(model->texture)
  4399              for(i = 0; i < model->numtexture; i++)
  4400                  if(model->texture[i].name)
  4401                      M3D_FREE(model->texture[i].name);
  4402          if(model->inlined)
  4403              for(i = 0; i < model->numinlined; i++) {
  4404                  if(model->inlined[i].name)
  4405                      M3D_FREE(model->inlined[i].name);
  4406                  if(model->inlined[i].data)
  4407                      M3D_FREE(model->inlined[i].data);
  4408              }
  4409          if(model->extra)
  4410              for(i = 0; i < model->numextra; i++)
  4411                  if(model->extra[i])
  4412                      M3D_FREE(model->extra[i]);
  4413          if(model->label)
  4414              for(i = 0; i < model->numlabel; i++) {
  4415                  if(model->label[i].name) {
  4416                      for(j = i + 1; j < model->numlabel; j++)
  4417                          if(model->label[j].name == model->label[i].name)
  4418                              model->label[j].name = NULL;
  4419                      M3D_FREE(model->label[i].name);
  4420                  }
  4421                  if(model->label[i].lang) {
  4422                      for(j = i + 1; j < model->numlabel; j++)
  4423                          if(model->label[j].lang == model->label[i].lang)
  4424                              model->label[j].lang = NULL;
  4425                      M3D_FREE(model->label[i].lang);
  4426                  }
  4427                  if(model->label[i].text)
  4428                      M3D_FREE(model->label[i].text);
  4429              }
  4430          if(model->preview.data)
  4431              M3D_FREE(model->preview.data);
  4432      }
  4433  #endif
  4434      if(model->flags & M3D_FLG_FREERAW) M3D_FREE(model->raw);
  4435  
  4436      if(model->tmap) M3D_FREE(model->tmap);
  4437      if(model->bone) {
  4438          for(i = 0; i < model->numbone; i++)
  4439              if(model->bone[i].weight)
  4440                  M3D_FREE(model->bone[i].weight);
  4441          M3D_FREE(model->bone);
  4442      }
  4443      if(model->skin) M3D_FREE(model->skin);
  4444      if(model->vertex) M3D_FREE(model->vertex);
  4445      if(model->face) M3D_FREE(model->face);
  4446      if(model->voxtype) {
  4447          for(i = 0; i < model->numvoxtype; i++)
  4448              if(model->voxtype[i].item)
  4449                  M3D_FREE(model->voxtype[i].item);
  4450          M3D_FREE(model->voxtype);
  4451      }
  4452      if(model->voxel) {
  4453          for(i = 0; i < model->numvoxel; i++)
  4454              if(model->voxel[i].data)
  4455                  M3D_FREE(model->voxel[i].data);
  4456          M3D_FREE(model->voxel);
  4457      }
  4458      if(model->shape) {
  4459          for(i = 0; i < model->numshape; i++) {
  4460              if(model->shape[i].cmd) {
  4461                  for(j = 0; j < model->shape[i].numcmd; j++)
  4462                      if(model->shape[i].cmd[j].arg) M3D_FREE(model->shape[i].cmd[j].arg);
  4463                  M3D_FREE(model->shape[i].cmd);
  4464              }
  4465          }
  4466          M3D_FREE(model->shape);
  4467      }
  4468      if(model->material && !(model->flags & M3D_FLG_MTLLIB)) {
  4469          for(i = 0; i < model->nummaterial; i++)
  4470              if(model->material[i].prop) M3D_FREE(model->material[i].prop);
  4471          M3D_FREE(model->material);
  4472      }
  4473      if(model->texture) {
  4474          for(i = 0; i < model->numtexture; i++)
  4475              if(model->texture[i].d) M3D_FREE(model->texture[i].d);
  4476          M3D_FREE(model->texture);
  4477      }
  4478      if(model->action) {
  4479          for(i = 0; i < model->numaction; i++) {
  4480              if(model->action[i].frame) {
  4481                  for(j = 0; j < model->action[i].numframe; j++)
  4482                      if(model->action[i].frame[j].transform) M3D_FREE(model->action[i].frame[j].transform);
  4483                  M3D_FREE(model->action[i].frame);
  4484              }
  4485          }
  4486          M3D_FREE(model->action);
  4487      }
  4488      if(model->label) M3D_FREE(model->label);
  4489      if(model->inlined) M3D_FREE(model->inlined);
  4490      if(model->extra) M3D_FREE(model->extra);
  4491      free(model);
  4492  }
  4493  #endif
  4494  
  4495  #ifdef M3D_EXPORTER
  4496  typedef struct {
  4497      char *str;
  4498      uint32_t offs;
  4499  } m3dstr_t;
  4500  
  4501  typedef struct {
  4502      m3dti_t data;
  4503      M3D_INDEX oldidx;
  4504      M3D_INDEX newidx;
  4505  } m3dtisave_t;
  4506  
  4507  typedef struct {
  4508      m3dv_t data;
  4509      M3D_INDEX oldidx;
  4510      M3D_INDEX newidx;
  4511      unsigned char norm;
  4512  } m3dvsave_t;
  4513  
  4514  typedef struct {
  4515      m3ds_t data;
  4516      M3D_INDEX oldidx;
  4517      M3D_INDEX newidx;
  4518  } m3dssave_t;
  4519  
  4520  typedef struct {
  4521      m3df_t data;
  4522      int group;
  4523      uint8_t opacity;
  4524  } m3dfsave_t;
  4525  
  4526  /* create unique list of strings */
  4527  static m3dstr_t *_m3d_addstr(m3dstr_t *str, uint32_t *numstr, char *s)
  4528  {
  4529      uint32_t i;
  4530      if(!s || !*s) return str;
  4531      if(str) {
  4532          for(i = 0; i < *numstr; i++)
  4533              if(str[i].str == s || !strcmp(str[i].str, s)) return str;
  4534      }
  4535      str = (m3dstr_t*)M3D_REALLOC(str, ((*numstr) + 1) * sizeof(m3dstr_t));
  4536      str[*numstr].str = s;
  4537      str[*numstr].offs = 0;
  4538      (*numstr)++;
  4539      return str;
  4540  }
  4541  
  4542  /* add strings to header */
  4543  m3dhdr_t *_m3d_addhdr(m3dhdr_t *h, m3dstr_t *s)
  4544  {
  4545      int i;
  4546      char *safe = _m3d_safestr(s->str, 0);
  4547      i = (int)strlen(safe);
  4548      h = (m3dhdr_t*)M3D_REALLOC(h, h->length + i+1);
  4549      if(!h) { M3D_FREE(safe); return NULL; }
  4550      memcpy((uint8_t*)h + h->length, safe, i+1);
  4551      s->offs = h->length - 16;
  4552      h->length += i+1;
  4553      M3D_FREE(safe);
  4554      return h;
  4555  }
  4556  
  4557  /* return offset of string */
  4558  static uint32_t _m3d_stridx(m3dstr_t *str, uint32_t numstr, char *s)
  4559  {
  4560      uint32_t i;
  4561      char *safe;
  4562      if(!s || !*s) return 0;
  4563      if(str) {
  4564          safe = _m3d_safestr(s, 0);
  4565          if(!safe) return 0;
  4566          if(!*safe) {
  4567              free(safe);
  4568              return 0;
  4569          }
  4570          for(i = 0; i < numstr; i++)
  4571              if(!strcmp(str[i].str, s)) {
  4572                  free(safe);
  4573                  return str[i].offs;
  4574              }
  4575          free(safe);
  4576      }
  4577      return 0;
  4578  }
  4579  
  4580  /* compare to faces by their material */
  4581  static int _m3d_facecmp(const void *a, const void *b) {
  4582      const m3dfsave_t *A = (const m3dfsave_t*)a, *B = (const m3dfsave_t*)b;
  4583      return A->group != B->group ? A->group - B->group : (A->opacity != B->opacity ? (int)B->opacity - (int)A->opacity :
  4584          (int)A->data.materialid - (int)B->data.materialid);
  4585  }
  4586  /* compare face groups */
  4587  static int _m3d_grpcmp(const void *a, const void *b) { return *((uint32_t*)a) - *((uint32_t*)b); }
  4588  /* compare UVs */
  4589  static int _m3d_ticmp(const void *a, const void *b) { return memcmp(a, b, sizeof(m3dti_t)); }
  4590  /* compare skin groups */
  4591  static int _m3d_skincmp(const void *a, const void *b) { return memcmp(a, b, sizeof(m3ds_t)); }
  4592  /* compare vertices */
  4593  static int _m3d_vrtxcmp(const void *a, const void *b) {
  4594      int c = memcmp(a, b, 3 * sizeof(M3D_FLOAT));
  4595      if(c) return c;
  4596      c = ((m3dvsave_t*)a)->norm - ((m3dvsave_t*)b)->norm;
  4597      if(c) return c;
  4598      return memcmp(a, b, sizeof(m3dv_t));
  4599  }
  4600  /* compare labels */
  4601  static _inline int _m3d_strcmp(char *a, char *b)
  4602  {
  4603      if(a == NULL && b != NULL) return -1;
  4604      if(a != NULL && b == NULL) return 1;
  4605      if(a == NULL && b == NULL) return 0;
  4606      return strcmp(a, b);
  4607  }
  4608  static int _m3d_lblcmp(const void *a, const void *b) {
  4609      const m3dl_t *A = (const m3dl_t*)a, *B = (const m3dl_t*)b;
  4610      int c = _m3d_strcmp(A->lang, B->lang);
  4611      if(!c) c = _m3d_strcmp(A->name, B->name);
  4612      if(!c) c = _m3d_strcmp(A->text, B->text);
  4613      return c;
  4614  }
  4615  /* compare two colors by HSV value */
  4616  _inline static int _m3d_cmapcmp(const void *a, const void *b)
  4617  {
  4618      uint8_t *A = (uint8_t*)a,  *B = (uint8_t*)b;
  4619      _register int m, vA, vB;
  4620      /* get HSV value for A */
  4621      m = A[2] < A[1]? A[2] : A[1]; if(A[0] < m) m = A[0];
  4622      vA = A[2] > A[1]? A[2] : A[1]; if(A[0] > vA) vA = A[0];
  4623      /* get HSV value for B */
  4624      m = B[2] < B[1]? B[2] : B[1]; if(B[0] < m) m = B[0];
  4625      vB = B[2] > B[1]? B[2] : B[1]; if(B[0] > vB) vB = B[0];
  4626      return vA - vB;
  4627  }
  4628  
  4629  /* create sorted list of colors */
  4630  static uint32_t *_m3d_addcmap(uint32_t *cmap, uint32_t *numcmap, uint32_t color)
  4631  {
  4632      uint32_t i;
  4633      if(cmap) {
  4634          for(i = 0; i < *numcmap; i++)
  4635              if(cmap[i] == color) return cmap;
  4636      }
  4637      cmap = (uint32_t*)M3D_REALLOC(cmap, ((*numcmap) + 1) * sizeof(uint32_t));
  4638      for(i = 0; i < *numcmap && _m3d_cmapcmp(&color, &cmap[i]) > 0; i++);
  4639      if(i < *numcmap) memmove(&cmap[i+1], &cmap[i], ((*numcmap) - i)*sizeof(uint32_t));
  4640      cmap[i] = color;
  4641      (*numcmap)++;
  4642      return cmap;
  4643  }
  4644  
  4645  /* look up a color and return its index */
  4646  static uint32_t _m3d_cmapidx(uint32_t *cmap, uint32_t numcmap, uint32_t color)
  4647  {
  4648      uint32_t i;
  4649      if(numcmap >= 65536)
  4650          return color;
  4651      for(i = 0; i < numcmap; i++)
  4652          if(cmap[i] == color) return i;
  4653      return 0;
  4654  }
  4655  
  4656  /* add index to output */
  4657  static unsigned char *_m3d_addidx(unsigned char *out, char type, uint32_t idx) {
  4658      switch(type) {
  4659          case 1: *out++ = (uint8_t)(idx); break;
  4660          case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break;
  4661          case 4: *((uint32_t*)out) = (uint32_t)(idx); out += 4; break;
  4662          /* case 0: case 8: break; */
  4663      }
  4664      return out;
  4665  }
  4666  
  4667  /* round a vertex position */
  4668  static void _m3d_round(int quality, m3dv_t *src, m3dv_t *dst)
  4669  {
  4670      _register int t;
  4671      /* copy additional attributes */
  4672      if(src != dst) memcpy(dst, src, sizeof(m3dv_t));
  4673      /* round according to quality */
  4674      switch(quality) {
  4675          case M3D_EXP_INT8:
  4676              t = (int)(src->x * 127 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)127.0;
  4677              t = (int)(src->y * 127 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)127.0;
  4678              t = (int)(src->z * 127 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)127.0;
  4679              t = (int)(src->w * 127 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)127.0;
  4680          break;
  4681          case M3D_EXP_INT16:
  4682              t = (int)(src->x * 32767 + (src->x >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->x = (M3D_FLOAT)t / (M3D_FLOAT)32767.0;
  4683              t = (int)(src->y * 32767 + (src->y >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->y = (M3D_FLOAT)t / (M3D_FLOAT)32767.0;
  4684              t = (int)(src->z * 32767 + (src->z >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->z = (M3D_FLOAT)t / (M3D_FLOAT)32767.0;
  4685              t = (int)(src->w * 32767 + (src->w >= 0 ? (M3D_FLOAT)0.5 : (M3D_FLOAT)-0.5)); dst->w = (M3D_FLOAT)t / (M3D_FLOAT)32767.0;
  4686          break;
  4687      }
  4688      if(dst->x == (M3D_FLOAT)-0.0) dst->x = (M3D_FLOAT)0.0;
  4689      if(dst->y == (M3D_FLOAT)-0.0) dst->y = (M3D_FLOAT)0.0;
  4690      if(dst->z == (M3D_FLOAT)-0.0) dst->z = (M3D_FLOAT)0.0;
  4691      if(dst->w == (M3D_FLOAT)-0.0) dst->w = (M3D_FLOAT)0.0;
  4692  }
  4693  
  4694  #ifdef M3D_ASCII
  4695  /* add a bone to ascii output */
  4696  static char *_m3d_prtbone(char *ptr, m3db_t *bone, M3D_INDEX numbone, M3D_INDEX parent, uint32_t level, M3D_INDEX *vrtxidx)
  4697  {
  4698      uint32_t i, j;
  4699      char *sn;
  4700  
  4701      if(level > M3D_BONEMAXLEVEL || !bone) return ptr;
  4702      for(i = 0; i < numbone; i++) {
  4703          if(bone[i].parent == parent) {
  4704              for(j = 0; j < level; j++) *ptr++ = '/';
  4705              sn = _m3d_safestr(bone[i].name, 0);
  4706              ptr += sprintf(ptr, "%d %d %s\r\n", vrtxidx[bone[i].pos], vrtxidx[bone[i].ori], sn);
  4707              M3D_FREE(sn);
  4708              ptr = _m3d_prtbone(ptr, bone, numbone, i, level + 1, vrtxidx);
  4709          }
  4710      }
  4711      return ptr;
  4712  }
  4713  #endif
  4714  
  4715  /**
  4716   * Function to encode an in-memory model into on storage Model 3D format
  4717   */
  4718  unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size)
  4719  {
  4720  #ifdef M3D_ASCII
  4721      const char *ol;
  4722      char *ptr;
  4723  #endif
  4724      char vc_s, vi_s, si_s, ci_s, ti_s, bi_s, nb_s, sk_s, fc_s, hi_s, fi_s, vd_s, vp_s;
  4725      char *sn = NULL, *sl = NULL, *sa = NULL, *sd = NULL;
  4726      unsigned char *out = NULL, *z = NULL, weights[M3D_NUMBONE < 8 ? 8 : M3D_NUMBONE], *norm = NULL;
  4727      unsigned int i, j, k, l, n, o, len, chunklen, *length;
  4728      int maxvox = 0, minvox = 0;
  4729      M3D_FLOAT scale = (M3D_FLOAT)0.0, min_x, max_x, min_y, max_y, min_z, max_z;
  4730      M3D_INDEX last, *vrtxidx = NULL, *mtrlidx = NULL, *tmapidx = NULL, *skinidx = NULL;
  4731  #ifdef M3D_VERTEXMAX
  4732      M3D_INDEX lastp;
  4733  #endif
  4734      uint32_t idx, numcmap = 0, *cmap = NULL, numvrtx = 0, maxvrtx = 0, numtmap = 0, maxtmap = 0, numproc = 0;
  4735      uint32_t numskin = 0, maxskin = 0, numstr = 0, maxt = 0, maxbone = 0, numgrp = 0, maxgrp = 0, *grpidx = NULL;
  4736      uint8_t *opa;
  4737      m3dcd_t *cd;
  4738      m3dc_t *cmd;
  4739      m3dstr_t *str = NULL;
  4740      m3dvsave_t *vrtx = NULL, vertex;
  4741      m3dtisave_t *tmap = NULL, tcoord;
  4742      m3dssave_t *skin = NULL, sk;
  4743      m3dfsave_t *face = NULL;
  4744      m3dhdr_t *h = NULL;
  4745      m3dm_t *m;
  4746      m3da_t *a;
  4747  
  4748      if(!model) {
  4749          if(size) *size = 0;
  4750          return NULL;
  4751      }
  4752      model->errcode = M3D_SUCCESS;
  4753  #ifdef M3D_ASCII
  4754      if(flags & M3D_EXP_ASCII) quality = M3D_EXP_DOUBLE;
  4755  #endif
  4756      vrtxidx = (M3D_INDEX*)M3D_MALLOC(model->numvertex * sizeof(M3D_INDEX));
  4757      if(!vrtxidx) goto memerr;
  4758      memset(vrtxidx, 255, model->numvertex * sizeof(M3D_INDEX));
  4759      if(model->numvertex && !(flags & M3D_EXP_NONORMAL)){
  4760          norm = (unsigned char*)M3D_MALLOC(model->numvertex * sizeof(unsigned char));
  4761          if(!norm) goto memerr;
  4762          memset(norm, 0, model->numvertex * sizeof(unsigned char));
  4763      }
  4764      if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) {
  4765          mtrlidx = (M3D_INDEX*)M3D_MALLOC(model->nummaterial * sizeof(M3D_INDEX));
  4766          if(!mtrlidx) goto memerr;
  4767          memset(mtrlidx, 255, model->nummaterial * sizeof(M3D_INDEX));
  4768          opa = (uint8_t*)M3D_MALLOC(model->nummaterial * 2 * sizeof(M3D_INDEX));
  4769          if(!opa) goto memerr;
  4770          memset(opa, 255, model->nummaterial * 2 * sizeof(M3D_INDEX));
  4771      }
  4772      if(model->numtmap && !(flags & M3D_EXP_NOTXTCRD)) {
  4773          tmapidx = (M3D_INDEX*)M3D_MALLOC(model->numtmap * sizeof(M3D_INDEX));
  4774          if(!tmapidx) goto memerr;
  4775          memset(tmapidx, 255, model->numtmap * sizeof(M3D_INDEX));
  4776      }
  4777      /** collect array elements that are actually referenced **/
  4778      if(!(flags & M3D_EXP_NOFACE)) {
  4779          /* face */
  4780          if(model->numface && model->face) {
  4781              M3D_LOG("Processing mesh face");
  4782              face = (m3dfsave_t*)M3D_MALLOC(model->numface * sizeof(m3dfsave_t));
  4783              if(!face) goto memerr;
  4784              for(i = 0; i < model->numface; i++) {
  4785                  memcpy(&face[i].data, &model->face[i], sizeof(m3df_t));
  4786                  face[i].group = 0;
  4787                  face[i].opacity = 255;
  4788                  if(!(flags & M3D_EXP_NOMATERIAL) && model->face[i].materialid < model->nummaterial) {
  4789                      if(model->material[model->face[i].materialid].numprop) {
  4790                          mtrlidx[model->face[i].materialid] = 0;
  4791                          if(opa[model->face[i].materialid * 2]) {
  4792                              m = &model->material[model->face[i].materialid];
  4793                              for(j = 0; j < m->numprop; j++)
  4794                                  if(m->prop[j].type == m3dp_Kd) {
  4795                                      opa[model->face[i].materialid * 2 + 1] = ((uint8_t*)&m->prop[j].value.color)[3];
  4796                                      break;
  4797                                  }
  4798                              for(j = 0; j < m->numprop; j++)
  4799                                  if(m->prop[j].type == m3dp_d) {
  4800                                      opa[model->face[i].materialid * 2 + 1] = (uint8_t)(m->prop[j].value.fnum * 255);
  4801                                      break;
  4802                                  }
  4803                              opa[model->face[i].materialid * 2] = 0;
  4804                          }
  4805                          face[i].opacity = opa[model->face[i].materialid * 2 + 1];
  4806                      } else
  4807                          face[i].data.materialid = M3D_UNDEF;
  4808                  }
  4809                  for(j = 0; j < 3; j++) {
  4810                      k = model->face[i].vertex[j];
  4811                      if(k < model->numvertex)
  4812                          vrtxidx[k] = 0;
  4813                      if(!(flags & M3D_EXP_NOCMAP)) {
  4814                          cmap = _m3d_addcmap(cmap, &numcmap, model->vertex[k].color);
  4815                          if(!cmap) goto memerr;
  4816                      }
  4817                      k = model->face[i].normal[j];
  4818                      if(k < model->numvertex && !(flags & M3D_EXP_NONORMAL)) {
  4819                          vrtxidx[k] = 0;
  4820                          norm[k] = 1;
  4821                      }
  4822                      k = model->face[i].texcoord[j];
  4823                      if(k < model->numtmap && !(flags & M3D_EXP_NOTXTCRD))
  4824                          tmapidx[k] = 0;
  4825  #ifdef M3D_VERTEXMAX
  4826                      k = model->face[i].vertmax[j];
  4827                      if(k < model->numvertex && !(flags & M3D_EXP_NOVRTMAX))
  4828                          vrtxidx[k] = 0;
  4829  #endif
  4830                  }
  4831                  /* convert from CW to CCW */
  4832                  if(flags & M3D_EXP_IDOSUCK) {
  4833                      j = face[i].data.vertex[1];
  4834                      face[i].data.vertex[1] = face[i].data.vertex[2];
  4835                      face[i].data.vertex[2] = j;
  4836                      j = face[i].data.normal[1];
  4837                      face[i].data.normal[1] = face[i].data.normal[2];
  4838                      face[i].data.normal[2] = j;
  4839                      j = face[i].data.texcoord[1];
  4840                      face[i].data.texcoord[1] = face[i].data.texcoord[2];
  4841                      face[i].data.texcoord[2] = j;
  4842  #ifdef M3D_VERTEXMAX
  4843                      j = face[i].data.vertmax[1];
  4844                      face[i].data.vertmax[1] = face[i].data.vertmax[2];
  4845                      face[i].data.vertmax[2] = j;
  4846  #endif
  4847                  }
  4848              }
  4849          }
  4850          if((model->numvoxtype && model->voxtype) || (model->numvoxel && model->voxel)) {
  4851              M3D_LOG("Processing voxel face");
  4852              for(i = 0; i < model->numvoxtype; i++) {
  4853                  str = _m3d_addstr(str, &numstr, model->voxtype[i].name);
  4854                  if(model->voxtype[i].name && !str) goto memerr;
  4855                  if(!(flags & M3D_EXP_NOCMAP)) {
  4856                      cmap = _m3d_addcmap(cmap, &numcmap, model->voxtype[i].color);
  4857                      if(!cmap) goto memerr;
  4858                  }
  4859                  for(j = 0; j < model->voxtype[i].numitem; j++) {
  4860                      str = _m3d_addstr(str, &numstr, model->voxtype[i].item[j].name);
  4861                      if(model->voxtype[i].item[j].name && !str) goto memerr;
  4862                  }
  4863              }
  4864              for(i = 0; i < model->numvoxel; i++) {
  4865                  str = _m3d_addstr(str, &numstr, model->voxel[i].name);
  4866                  if(model->voxel[i].name && !str) goto memerr;
  4867                  if(model->voxel[i].x < minvox) minvox = model->voxel[i].x;
  4868                  if(model->voxel[i].x + (int)model->voxel[i].w > maxvox) maxvox = model->voxel[i].x + model->voxel[i].w;
  4869                  if(model->voxel[i].y < minvox) minvox = model->voxel[i].y;
  4870                  if(model->voxel[i].y + (int)model->voxel[i].h > maxvox) maxvox = model->voxel[i].y + model->voxel[i].h;
  4871                  if(model->voxel[i].z < minvox) minvox = model->voxel[i].z;
  4872                  if(model->voxel[i].z + (int)model->voxel[i].d > maxvox) maxvox = model->voxel[i].z + model->voxel[i].d;
  4873              }
  4874          }
  4875          if(model->numshape && model->shape) {
  4876              M3D_LOG("Processing shape face");
  4877              for(i = 0; i < model->numshape; i++) {
  4878                  if(!model->shape[i].numcmd) continue;
  4879                  str = _m3d_addstr(str, &numstr, model->shape[i].name);
  4880                  if(model->shape[i].name && !str) goto memerr;
  4881                  for(j = 0; j < model->shape[i].numcmd; j++) {
  4882                      cmd = &model->shape[i].cmd[j];
  4883                      if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg)
  4884                          continue;
  4885                      if(cmd->type == m3dc_mesh) {
  4886                          if(numgrp + 2 < maxgrp) {
  4887                              maxgrp += 1024;
  4888                              grpidx = (uint32_t*)realloc(grpidx, maxgrp * sizeof(uint32_t));
  4889                              if(!grpidx) goto memerr;
  4890                              if(!numgrp) {
  4891                                  grpidx[0] = 0;
  4892                                  grpidx[1] = model->numface;
  4893                                  numgrp += 2;
  4894                              }
  4895                          }
  4896                          grpidx[numgrp + 0] = cmd->arg[0];
  4897                          grpidx[numgrp + 1] = cmd->arg[0] + cmd->arg[1];
  4898                          numgrp += 2;
  4899                      }
  4900                      cd = &m3d_commandtypes[cmd->type];
  4901                      for(k = n = 0, l = cd->p; k < l; k++)
  4902                          switch(cd->a[((k - n) % (cd->p - n)) + n]) {
  4903                              case m3dcp_mi_t:
  4904                                  if(!(flags & M3D_EXP_NOMATERIAL) && cmd->arg[k] < model->nummaterial)
  4905                                      mtrlidx[cmd->arg[k]] = 0;
  4906                              break;
  4907                              case m3dcp_ti_t:
  4908                                  if(!(flags & M3D_EXP_NOTXTCRD) && cmd->arg[k] < model->numtmap)
  4909                                      tmapidx[cmd->arg[k]] = 0;
  4910                              break;
  4911                              case m3dcp_qi_t:
  4912                              case m3dcp_vi_t:
  4913                                  if(cmd->arg[k] < model->numvertex)
  4914                                      vrtxidx[cmd->arg[k]] = 0;
  4915                              break;
  4916                              case m3dcp_va_t:
  4917                                  n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1);
  4918                              break;
  4919                          }
  4920                  }
  4921              }
  4922          }
  4923          if(model->numface && face) {
  4924              if(numgrp && grpidx) {
  4925                  qsort(grpidx, numgrp, sizeof(uint32_t), _m3d_grpcmp);
  4926                  for(i = j = 0; i < model->numface && j < numgrp; i++) {
  4927                      while(j < numgrp && grpidx[j] < i) j++;
  4928                      face[i].group = j;
  4929                  }
  4930              }
  4931              qsort(face, model->numface, sizeof(m3dfsave_t), _m3d_facecmp);
  4932          }
  4933          if(grpidx) { M3D_FREE(grpidx); grpidx = NULL; }
  4934          if(model->numlabel && model->label) {
  4935              M3D_LOG("Processing annotation labels");
  4936              for(i = 0; i < model->numlabel; i++) {
  4937                  str = _m3d_addstr(str, &numstr, model->label[i].name);
  4938                  str = _m3d_addstr(str, &numstr, model->label[i].lang);
  4939                  str = _m3d_addstr(str, &numstr, model->label[i].text);
  4940                  if(!(flags & M3D_EXP_NOCMAP)) {
  4941                      cmap = _m3d_addcmap(cmap, &numcmap, model->label[i].color);
  4942                      if(!cmap) goto memerr;
  4943                  }
  4944                  if(model->label[i].vertexid < model->numvertex)
  4945                      vrtxidx[model->label[i].vertexid] = 0;
  4946              }
  4947              qsort(model->label, model->numlabel, sizeof(m3dl_t), _m3d_lblcmp);
  4948          }
  4949      } else if(!(flags & M3D_EXP_NOMATERIAL)) {
  4950          /* without a face, simply add all materials, because it can be an mtllib */
  4951          for(i = 0; i < model->nummaterial; i++)
  4952              mtrlidx[i] = i;
  4953      }
  4954      /* bind-pose skeleton */
  4955      if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) {
  4956          M3D_LOG("Processing bones");
  4957          for(i = 0; i < model->numbone; i++) {
  4958              str = _m3d_addstr(str, &numstr, model->bone[i].name);
  4959              if(!str) goto memerr;
  4960              k = model->bone[i].pos;
  4961              if(k < model->numvertex)
  4962                  vrtxidx[k] = 0;
  4963              k = model->bone[i].ori;
  4964              if(k < model->numvertex)
  4965                  vrtxidx[k] = 0;
  4966          }
  4967      }
  4968      /* actions, animated skeleton poses */
  4969      if(model->numaction && model->action && !(flags & M3D_EXP_NOACTION)) {
  4970          M3D_LOG("Processing action list");
  4971          for(j = 0; j < model->numaction; j++) {
  4972              a = &model->action[j];
  4973              str = _m3d_addstr(str, &numstr, a->name);
  4974              if(!str) goto memerr;
  4975              if(a->numframe > 65535) a->numframe = 65535;
  4976              for(i = 0; i < a->numframe; i++) {
  4977                  for(l = 0; l < a->frame[i].numtransform; l++) {
  4978                      k = a->frame[i].transform[l].pos;
  4979                      if(k < model->numvertex)
  4980                          vrtxidx[k] = 0;
  4981                      k = a->frame[i].transform[l].ori;
  4982                      if(k < model->numvertex)
  4983                          vrtxidx[k] = 0;
  4984                  }
  4985                  if(l > maxt) maxt = l;
  4986              }
  4987          }
  4988      }
  4989      /* add colors to color map and texture names to string table */
  4990      if(!(flags & M3D_EXP_NOMATERIAL)) {
  4991          M3D_LOG("Processing materials");
  4992          for(i = k = 0; i < model->nummaterial; i++) {
  4993              if(mtrlidx[i] == M3D_UNDEF || !model->material[i].numprop) continue;
  4994              mtrlidx[i] = k++;
  4995              m = &model->material[i];
  4996              str = _m3d_addstr(str, &numstr, m->name);
  4997              if(!str) goto memerr;
  4998              if(m->prop)
  4999                  for(j = 0; j < m->numprop; j++) {
  5000                      if(!(flags & M3D_EXP_NOCMAP) && m->prop[j].type < 128) {
  5001                          for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++) {
  5002                              if(m->prop[j].type == m3d_propertytypes[l].id && m3d_propertytypes[l].format == m3dpf_color) {
  5003                                  ((uint8_t*)&m->prop[j].value.color)[3] = opa[i * 2 + 1];
  5004                                  cmap = _m3d_addcmap(cmap, &numcmap, m->prop[j].value.color);
  5005                                  if(!cmap) goto memerr;
  5006                                  break;
  5007                              }
  5008                          }
  5009                      }
  5010                      if(m->prop[j].type >= 128 && m->prop[j].value.textureid < model->numtexture &&
  5011                          model->texture[m->prop[j].value.textureid].name) {
  5012                          str = _m3d_addstr(str, &numstr, model->texture[m->prop[j].value.textureid].name);
  5013                          if(!str) goto memerr;
  5014                      }
  5015                  }
  5016          }
  5017      }
  5018      /* if there's only one black color, don't store it */
  5019      if(numcmap == 1 && cmap && !cmap[0]) numcmap = 0;
  5020  
  5021      /** compress lists **/
  5022      if(model->numtmap && !(flags & M3D_EXP_NOTXTCRD)) {
  5023          M3D_LOG("Compressing tmap");
  5024          tmap = (m3dtisave_t*)M3D_MALLOC(model->numtmap * sizeof(m3dtisave_t));
  5025          if(!tmap) goto memerr;
  5026          for(i = 0; i < model->numtmap; i++) {
  5027              if(tmapidx[i] == M3D_UNDEF) continue;
  5028              switch(quality) {
  5029                  case M3D_EXP_INT8:
  5030                      l = (unsigned int)(model->tmap[i].u * 255); tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)255.0;
  5031                      l = (unsigned int)(model->tmap[i].v * 255); tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)255.0;
  5032                  break;
  5033                  case M3D_EXP_INT16:
  5034                      l = (unsigned int)(model->tmap[i].u * 65535); tcoord.data.u = (M3D_FLOAT)l / (M3D_FLOAT)65535.0;
  5035                      l = (unsigned int)(model->tmap[i].v * 65535); tcoord.data.v = (M3D_FLOAT)l / (M3D_FLOAT)65535.0;
  5036                  break;
  5037                  default:
  5038                      tcoord.data.u = model->tmap[i].u;
  5039                      tcoord.data.v = model->tmap[i].v;
  5040                  break;
  5041              }
  5042              if(flags & M3D_EXP_FLIPTXTCRD)
  5043                  tcoord.data.v = (M3D_FLOAT)1.0 - tcoord.data.v;
  5044              tcoord.oldidx = i;
  5045              memcpy(&tmap[numtmap++], &tcoord, sizeof(m3dtisave_t));
  5046          }
  5047          if(numtmap) {
  5048              qsort(tmap, numtmap, sizeof(m3dtisave_t), _m3d_ticmp);
  5049              memcpy(&tcoord.data, &tmap[0], sizeof(m3dti_t));
  5050              for(i = 0; i < numtmap; i++) {
  5051                  if(memcmp(&tcoord.data, &tmap[i].data, sizeof(m3dti_t))) {
  5052                      memcpy(&tcoord.data, &tmap[i].data, sizeof(m3dti_t));
  5053                      maxtmap++;
  5054                  }
  5055                  tmap[i].newidx = maxtmap;
  5056                  tmapidx[tmap[i].oldidx] = maxtmap;
  5057              }
  5058              maxtmap++;
  5059          }
  5060      }
  5061      if(model->numskin && model->skin && !(flags & M3D_EXP_NOBONE)) {
  5062          M3D_LOG("Compressing skin");
  5063          skinidx = (M3D_INDEX*)M3D_MALLOC(model->numskin * sizeof(M3D_INDEX));
  5064          if(!skinidx) goto memerr;
  5065          skin = (m3dssave_t*)M3D_MALLOC(model->numskin * sizeof(m3dssave_t));
  5066          if(!skin) goto memerr;
  5067          memset(skinidx, 255, model->numskin * sizeof(M3D_INDEX));
  5068          for(i = 0; i < model->numvertex; i++) {
  5069              if(vrtxidx[i] != M3D_UNDEF && model->vertex[i].skinid < model->numskin)
  5070                  skinidx[model->vertex[i].skinid] = 0;
  5071          }
  5072          for(i = 0; i < model->numskin; i++) {
  5073              if(skinidx[i] == M3D_UNDEF) continue;
  5074              memset(&sk, 0, sizeof(m3dssave_t));
  5075              for(j = 0, min_x = (M3D_FLOAT)0.0; j < M3D_NUMBONE && model->skin[i].boneid[j] != M3D_UNDEF &&
  5076                  model->skin[i].weight[j] > (M3D_FLOAT)0.0; j++) {
  5077                      sk.data.boneid[j] = model->skin[i].boneid[j];
  5078                      sk.data.weight[j] = model->skin[i].weight[j];
  5079                      min_x += sk.data.weight[j];
  5080              }
  5081              if(j > maxbone) maxbone = j;
  5082              if(min_x != (M3D_FLOAT)1.0 && min_x != (M3D_FLOAT)0.0)
  5083                  for(j = 0; j < M3D_NUMBONE && sk.data.weight[j] > (M3D_FLOAT)0.0; j++)
  5084                      sk.data.weight[j] /= min_x;
  5085              sk.oldidx = i;
  5086              memcpy(&skin[numskin++], &sk, sizeof(m3dssave_t));
  5087          }
  5088          if(numskin) {
  5089              qsort(skin, numskin, sizeof(m3dssave_t), _m3d_skincmp);
  5090              memcpy(&sk.data, &skin[0].data, sizeof(m3ds_t));
  5091              for(i = 0; i < numskin; i++) {
  5092                  if(memcmp(&sk.data, &skin[i].data, sizeof(m3ds_t))) {
  5093                      memcpy(&sk.data, &skin[i].data, sizeof(m3ds_t));
  5094                      maxskin++;
  5095                  }
  5096                  skin[i].newidx = maxskin;
  5097                  skinidx[skin[i].oldidx] = maxskin;
  5098              }
  5099              maxskin++;
  5100          }
  5101      }
  5102  
  5103      M3D_LOG("Compressing vertex list");
  5104      min_x = min_y = min_z = (M3D_FLOAT)1e10;
  5105      max_x = max_y = max_z = (M3D_FLOAT)-1e10;
  5106      if(vrtxidx) {
  5107          vrtx = (m3dvsave_t*)M3D_MALLOC(model->numvertex * sizeof(m3dvsave_t));
  5108          if(!vrtx) goto memerr;
  5109          for(i = numvrtx = 0; i < model->numvertex; i++) {
  5110              if(vrtxidx[i] == M3D_UNDEF) continue;
  5111              _m3d_round(quality, &model->vertex[i], &vertex.data);
  5112              vertex.norm = norm ? norm[i] : 0;
  5113              if(vertex.data.skinid != M3D_INDEXMAX && !vertex.norm) {
  5114                  vertex.data.skinid = vertex.data.skinid != M3D_UNDEF && skinidx ? skinidx[vertex.data.skinid] : M3D_UNDEF;
  5115                  if(vertex.data.x > max_x) max_x = vertex.data.x;
  5116                  if(vertex.data.x < min_x) min_x = vertex.data.x;
  5117                  if(vertex.data.y > max_y) max_y = vertex.data.y;
  5118                  if(vertex.data.y < min_y) min_y = vertex.data.y;
  5119                  if(vertex.data.z > max_z) max_z = vertex.data.z;
  5120                  if(vertex.data.z < min_z) min_z = vertex.data.z;
  5121              }
  5122  #ifdef M3D_VERTEXTYPE
  5123              vertex.data.type = 0;
  5124  #endif
  5125              vertex.oldidx = i;
  5126              memcpy(&vrtx[numvrtx++], &vertex, sizeof(m3dvsave_t));
  5127          }
  5128          if(numvrtx) {
  5129              qsort(vrtx, numvrtx, sizeof(m3dvsave_t), _m3d_vrtxcmp);
  5130              memcpy(&vertex.data, &vrtx[0].data, sizeof(m3dv_t));
  5131              for(i = 0; i < numvrtx; i++) {
  5132                  if(memcmp(&vertex.data, &vrtx[i].data, vrtx[i].norm ? 3 * sizeof(M3D_FLOAT) : sizeof(m3dv_t))) {
  5133                      memcpy(&vertex.data, &vrtx[i].data, sizeof(m3dv_t));
  5134                      maxvrtx++;
  5135                  }
  5136                  vrtx[i].newidx = maxvrtx;
  5137                  vrtxidx[vrtx[i].oldidx] = maxvrtx;
  5138              }
  5139              maxvrtx++;
  5140          }
  5141      }
  5142      if(norm) { M3D_FREE(norm); norm = NULL; }
  5143  
  5144      /* normalize to bounding cube */
  5145      if(numvrtx && !(flags & M3D_EXP_NORECALC)) {
  5146          M3D_LOG("Normalizing coordinates");
  5147          if(min_x < (M3D_FLOAT)0.0) min_x = -min_x;
  5148          if(max_x < (M3D_FLOAT)0.0) max_x = -max_x;
  5149          if(min_y < (M3D_FLOAT)0.0) min_y = -min_y;
  5150          if(max_y < (M3D_FLOAT)0.0) max_y = -max_y;
  5151          if(min_z < (M3D_FLOAT)0.0) min_z = -min_z;
  5152          if(max_z < (M3D_FLOAT)0.0) max_z = -max_z;
  5153          scale = min_x;
  5154          if(max_x > scale) scale = max_x;
  5155          if(min_y > scale) scale = min_y;
  5156          if(max_y > scale) scale = max_y;
  5157          if(min_z > scale) scale = min_z;
  5158          if(max_z > scale) scale = max_z;
  5159          if(scale <= (M3D_FLOAT)0.0) scale = (M3D_FLOAT)1.0;
  5160          if(scale != (M3D_FLOAT)1.0) {
  5161              for(i = 0; i < numvrtx; i++) {
  5162                  if(vrtx[i].data.skinid == M3D_INDEXMAX) continue;
  5163                  vrtx[i].data.x /= scale;
  5164                  vrtx[i].data.y /= scale;
  5165                  vrtx[i].data.z /= scale;
  5166              }
  5167          }
  5168      }
  5169      if(model->scale > (M3D_FLOAT)0.0) scale = model->scale;
  5170      if(scale <= (M3D_FLOAT)0.0) scale = (M3D_FLOAT)1.0;
  5171  
  5172      /* meta info */
  5173      sn = _m3d_safestr(model->name && *model->name ? model->name : (char*)"(noname)", 2);
  5174      sl = _m3d_safestr(model->license ? model->license : (char*)"MIT", 2);
  5175      sa = _m3d_safestr(model->author ? model->author : getenv("LOGNAME"), 2);
  5176      if(!sn || !sl || !sa) {
  5177  memerr: if(vrtxidx) M3D_FREE(vrtxidx);
  5178          if(mtrlidx) M3D_FREE(mtrlidx);
  5179          if(tmapidx) M3D_FREE(tmapidx);
  5180          if(skinidx) M3D_FREE(skinidx);
  5181          if(grpidx) M3D_FREE(grpidx);
  5182          if(norm) M3D_FREE(norm);
  5183          if(face) M3D_FREE(face);
  5184          if(cmap) M3D_FREE(cmap);
  5185          if(tmap) M3D_FREE(tmap);
  5186          if(skin) M3D_FREE(skin);
  5187          if(str) M3D_FREE(str);
  5188          if(vrtx) M3D_FREE(vrtx);
  5189          if(sn) M3D_FREE(sn);
  5190          if(sl) M3D_FREE(sl);
  5191          if(sa) M3D_FREE(sa);
  5192          if(sd) M3D_FREE(sd);
  5193          if(out) M3D_FREE(out);
  5194          if(h) M3D_FREE(h);
  5195          M3D_LOG("Out of memory");
  5196          model->errcode = M3D_ERR_ALLOC;
  5197          return NULL;
  5198      }
  5199  
  5200      M3D_LOG("Serializing model");
  5201  #ifdef M3D_ASCII
  5202      if(flags & M3D_EXP_ASCII) {
  5203          /* use CRLF to make model creators on Win happy... */
  5204          sd = _m3d_safestr(model->desc, 1);
  5205          if(!sd) goto memerr;
  5206          ol = setlocale(LC_NUMERIC, NULL);
  5207          setlocale(LC_NUMERIC, "C");
  5208          /* header */
  5209          len = 64 + (unsigned int)(strlen(sn) + strlen(sl) + strlen(sa) + strlen(sd));
  5210          out = (unsigned char*)M3D_MALLOC(len);
  5211          if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5212          ptr = (char*)out;
  5213          ptr += sprintf(ptr, "3dmodel %g\r\n%s\r\n%s\r\n%s\r\n%s\r\n\r\n", scale,
  5214              sn, sl, sa, sd);
  5215          M3D_FREE(sl); M3D_FREE(sa); M3D_FREE(sd);
  5216          sl = sa = sd = NULL;
  5217          /* preview chunk */
  5218          if(model->preview.data && model->preview.length) {
  5219              sl = _m3d_safestr(sn, 0);
  5220              if(sl) {
  5221                  ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)20 + strlen(sl));
  5222                  out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5223                  if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5224                  ptr += sprintf(ptr, "Preview\r\n%s.png\r\n\r\n", sl);
  5225                  M3D_FREE(sl); sl = NULL;
  5226              }
  5227          }
  5228          M3D_FREE(sn);  sn = NULL;
  5229          /* texture map */
  5230          if(numtmap && tmap && !(flags & M3D_EXP_NOTXTCRD) && !(flags & M3D_EXP_NOFACE)) {
  5231              ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(maxtmap * 32) + (uintptr_t)12);
  5232              out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5233              if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5234              ptr += sprintf(ptr, "Textmap\r\n");
  5235              last = M3D_UNDEF;
  5236              for(i = 0; i < numtmap; i++) {
  5237                  if(tmap[i].newidx == last) continue;
  5238                  last = tmap[i].newidx;
  5239                  ptr += sprintf(ptr, "%g %g\r\n", tmap[i].data.u, tmap[i].data.v);
  5240              }
  5241              ptr += sprintf(ptr, "\r\n");
  5242          }
  5243          /* vertex chunk */
  5244          if(numvrtx && vrtx && !(flags & M3D_EXP_NOFACE)) {
  5245              ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(maxvrtx * 128) + (uintptr_t)10);
  5246              out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5247              if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5248              ptr += sprintf(ptr, "Vertex\r\n");
  5249              last = M3D_UNDEF;
  5250              for(i = 0; i < numvrtx; i++) {
  5251                  if(vrtx[i].newidx == last) continue;
  5252                  last = vrtx[i].newidx;
  5253                  ptr += sprintf(ptr, "%g %g %g %g", vrtx[i].data.x, vrtx[i].data.y, vrtx[i].data.z, vrtx[i].data.w);
  5254                  if(!(flags & M3D_EXP_NOCMAP) && vrtx[i].data.color)
  5255                      ptr += sprintf(ptr, " #%08x", vrtx[i].data.color);
  5256                  if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin && vrtx[i].data.skinid < M3D_INDEXMAX) {
  5257                      if(skin[vrtx[i].data.skinid].data.weight[0] == (M3D_FLOAT)1.0)
  5258                          ptr += sprintf(ptr, " %d", skin[vrtx[i].data.skinid].data.boneid[0]);
  5259                      else
  5260                          for(j = 0; j < M3D_NUMBONE && skin[vrtx[i].data.skinid].data.boneid[j] != M3D_UNDEF &&
  5261                              skin[vrtx[i].data.skinid].data.weight[j] > (M3D_FLOAT)0.0; j++)
  5262                              ptr += sprintf(ptr, " %d:%g", skin[vrtx[i].data.skinid].data.boneid[j],
  5263                                  skin[vrtx[i].data.skinid].data.weight[j]);
  5264                  }
  5265                  ptr += sprintf(ptr, "\r\n");
  5266              }
  5267              ptr += sprintf(ptr, "\r\n");
  5268          }
  5269          /* bones chunk */
  5270          if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) {
  5271              ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)9);
  5272              for(i = 0; i < model->numbone; i++) {
  5273                  len += (unsigned int)strlen(model->bone[i].name) + 128;
  5274              }
  5275              out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5276              if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5277              ptr += sprintf(ptr, "Bones\r\n");
  5278              ptr = _m3d_prtbone(ptr, model->bone, model->numbone, M3D_UNDEF, 0, vrtxidx);
  5279              ptr += sprintf(ptr, "\r\n");
  5280          }
  5281          /* materials */
  5282          if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) {
  5283              for(j = 0; j < model->nummaterial; j++) {
  5284                  if(mtrlidx[j] == M3D_UNDEF || !model->material[j].numprop || !model->material[j].prop) continue;
  5285                  m = &model->material[j];
  5286                  sn = _m3d_safestr(m->name, 0);
  5287                  if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5288                  ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)12);
  5289                  for(i = 0; i < m->numprop; i++) {
  5290                      if(m->prop[i].type < 128)
  5291                          len += 32;
  5292                      else if(m->prop[i].value.textureid < model->numtexture && model->texture[m->prop[i].value.textureid].name)
  5293                          len += (unsigned int)strlen(model->texture[m->prop[i].value.textureid].name) + 16;
  5294                  }
  5295                  out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5296                  if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5297                  ptr += sprintf(ptr, "Material %s\r\n", sn);
  5298                  M3D_FREE(sn); sn = NULL;
  5299                  for(i = 0; i < m->numprop; i++) {
  5300                      k = 256;
  5301                      if(m->prop[i].type >= 128) {
  5302                          for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++)
  5303                              if(m->prop[i].type == m3d_propertytypes[l].id) {
  5304                                  sn = m3d_propertytypes[l].key;
  5305                                  break;
  5306                              }
  5307                          if(!sn)
  5308                              for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++)
  5309                                  if(m->prop[i].type - 128 == m3d_propertytypes[l].id) {
  5310                                      sn = m3d_propertytypes[l].key;
  5311                                      break;
  5312                                  }
  5313                          k = sn ? m3dpf_map : 256;
  5314                      } else {
  5315                          for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++)
  5316                              if(m->prop[i].type == m3d_propertytypes[l].id) {
  5317                                  sn = m3d_propertytypes[l].key;
  5318                                  k = m3d_propertytypes[l].format;
  5319                                  break;
  5320                              }
  5321                      }
  5322                      switch(k) {
  5323                          case m3dpf_color: ptr += sprintf(ptr, "%s #%08x\r\n", sn, m->prop[i].value.color); break;
  5324                          case m3dpf_uint8:
  5325                          case m3dpf_uint16:
  5326                          case m3dpf_uint32: ptr += sprintf(ptr, "%s %d\r\n", sn, m->prop[i].value.num); break;
  5327                          case m3dpf_float:  ptr += sprintf(ptr, "%s %g\r\n", sn, m->prop[i].value.fnum); break;
  5328                          case m3dpf_map:
  5329                              if(m->prop[i].value.textureid < model->numtexture &&
  5330                                  model->texture[m->prop[i].value.textureid].name) {
  5331                                  sl = _m3d_safestr(model->texture[m->prop[i].value.textureid].name, 0);
  5332                                  if(!sl) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5333                                  if(*sl)
  5334                                      ptr += sprintf(ptr, "map_%s %s\r\n", sn, sl);
  5335                                  M3D_FREE(sn); M3D_FREE(sl); sl = NULL;
  5336                              }
  5337                          break;
  5338                      }
  5339                      sn = NULL;
  5340                  }
  5341                  ptr += sprintf(ptr, "\r\n");
  5342              }
  5343          }
  5344          /* procedural face */
  5345          if(model->numinlined && model->inlined && !(flags & M3D_EXP_NOFACE)) {
  5346              /* all inlined assets which are not textures should be procedural surfaces */
  5347              for(j = 0; j < model->numinlined; j++) {
  5348                  if(!model->inlined[j].name || !*model->inlined[j].name || !model->inlined[j].length || !model->inlined[j].data ||
  5349                   (model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' && model->inlined[j].data[3] == 'G'))
  5350                      continue;
  5351                  for(i = k = 0; i < model->numtexture; i++) {
  5352                      if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; }
  5353                  }
  5354                  if(k) continue;
  5355                  sn = _m3d_safestr(model->inlined[j].name, 0);
  5356                  if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5357                  ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)18);
  5358                  out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5359                  if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5360                  ptr += sprintf(ptr, "Procedural\r\n%s\r\n\r\n", sn);
  5361                  M3D_FREE(sn); sn = NULL;
  5362              }
  5363          }
  5364          /* mesh face */
  5365          if(model->numface && face && !(flags & M3D_EXP_NOFACE)) {
  5366              ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(model->numface * 128) + (uintptr_t)6);
  5367              last = M3D_UNDEF;
  5368  #ifdef M3D_VERTEXMAX
  5369              lastp = M3D_UNDEF;
  5370  #endif
  5371              if(!(flags & M3D_EXP_NOMATERIAL))
  5372                  for(i = 0; i < model->numface; i++) {
  5373                      j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : M3D_UNDEF;
  5374                      if(j != last) {
  5375                          last = j;
  5376                          if(last < model->nummaterial)
  5377                              len += (unsigned int)strlen(model->material[last].name);
  5378                          len += 6;
  5379                      }
  5380  #ifdef M3D_VERTEXMAX
  5381                      j = face[i].data.paramid < model->numparam ? face[i].data.paramid : M3D_UNDEF;
  5382                      if(j != lastp) {
  5383                          lastp = j;
  5384                          if(lastp < model->numparam)
  5385                              len += (unsigned int)strlen(model->param[lastp].name);
  5386                          len += 6;
  5387                      }
  5388  #endif
  5389                  }
  5390              out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5391              if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5392              ptr += sprintf(ptr, "Mesh\r\n");
  5393              last = M3D_UNDEF;
  5394  #ifdef M3D_VERTEXMAX
  5395              lastp = M3D_UNDEF;
  5396  #endif
  5397              for(i = 0; i < model->numface; i++) {
  5398                  j = face[i].data.materialid < model->nummaterial ? face[i].data.materialid : M3D_UNDEF;
  5399                  if(!(flags & M3D_EXP_NOMATERIAL) && j != last) {
  5400                      last = j;
  5401                      if(last < model->nummaterial) {
  5402                          sn = _m3d_safestr(model->material[last].name, 0);
  5403                          if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5404                          ptr += sprintf(ptr, "use %s\r\n", sn);
  5405                          M3D_FREE(sn); sn = NULL;
  5406                      } else
  5407                          ptr += sprintf(ptr, "use\r\n");
  5408                  }
  5409  #ifdef M3D_VERTEXMAX
  5410                  j = face[i].data.paramid < model->numparam ? face[i].data.paramid : M3D_UNDEF;
  5411                  if(!(flags & M3D_EXP_NOVRTMAX) && j != lastp) {
  5412                      lastp = j;
  5413                      if(lastp < model->numparam) {
  5414                          sn = _m3d_safestr(model->param[lastp].name, 0);
  5415                          if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5416                          ptr += sprintf(ptr, "par %s\r\n", sn);
  5417                          M3D_FREE(sn); sn = NULL;
  5418                      } else
  5419                          ptr += sprintf(ptr, "par\r\n");
  5420                  }
  5421  #endif
  5422                  /* hardcoded triangles. Should be repeated as many times as the number of edges in polygon */
  5423                  for(j = 0; j < 3; j++) {
  5424                      ptr += sprintf(ptr, "%s%d", j?" ":"", vrtxidx[face[i].data.vertex[j]]);
  5425                      k = l = M3D_NOTDEFINED;
  5426                      if(!(flags & M3D_EXP_NOTXTCRD) && (face[i].data.texcoord[j] != M3D_UNDEF) &&
  5427                          (tmapidx[face[i].data.texcoord[j]] != M3D_UNDEF)) {
  5428                              k = tmapidx[face[i].data.texcoord[j]];
  5429                              ptr += sprintf(ptr, "/%d", k);
  5430                      }
  5431                      if(!(flags & M3D_EXP_NONORMAL) && (face[i].data.normal[j] != M3D_UNDEF)) {
  5432                          l = vrtxidx[face[i].data.normal[j]];
  5433                          ptr += sprintf(ptr, "%s/%d", k == M3D_NOTDEFINED? "/" : "", l);
  5434                      }
  5435  #ifdef M3D_VERTEXMAX
  5436                      if(!(flags & M3D_EXP_NOVRTMAX) && (face[i].data.vertmax[j] != M3D_UNDEF)) {
  5437                          ptr += sprintf(ptr, "%s%s/%d", k == M3D_NOTDEFINED? "/" : "", l == M3D_NOTDEFINED? "/" : "",
  5438                              vrtxidx[face[i].data.vertmax[j]]);
  5439                      }
  5440  #endif
  5441                  }
  5442                  ptr += sprintf(ptr, "\r\n");
  5443              }
  5444              ptr += sprintf(ptr, "\r\n");
  5445          }
  5446          /* voxel face */
  5447          if(model->numvoxtype && model->voxtype && !(flags & M3D_EXP_NOFACE)) {
  5448              ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)(model->numvoxtype * 128) + (uintptr_t)10);
  5449              for(i = 0; i < model->numvoxtype; i++) {
  5450                  if(model->voxtype[i].name) len += (unsigned int)strlen(model->voxtype[i].name);
  5451                  for(j = 0; j < model->voxtype[i].numitem; j++)
  5452                      if(model->voxtype[i].item[j].name)
  5453                          len += (unsigned int)strlen(model->voxtype[i].item[j].name) + 6;
  5454              }
  5455              out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5456              if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5457              ptr += sprintf(ptr, "VoxTypes\r\n");
  5458              for(i = 0; i < model->numvoxtype; i++) {
  5459                  ptr += sprintf(ptr, "#%08x", model->voxtype[i].color);
  5460                  if(model->voxtype[i].rotation)
  5461                      ptr += sprintf(ptr, "/%02x", model->voxtype[i].rotation);
  5462                  if(model->voxtype[i].voxshape)
  5463                      ptr += sprintf(ptr, "%s/%03x", model->voxtype[i].rotation ? "" : "/", model->voxtype[i].voxshape);
  5464                  sn = _m3d_safestr(model->voxtype[i].name, 0);
  5465                  if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5466                  ptr += sprintf(ptr, " %s", sn && sn[0] ? sn : "-");
  5467                  M3D_FREE(sn); sn = NULL;
  5468                  if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin && model->voxtype[i].skinid < M3D_INDEXMAX) {
  5469                      if(skin[skinidx[model->voxtype[i].skinid]].data.weight[0] == (M3D_FLOAT)1.0)
  5470                          ptr += sprintf(ptr, " %d", skin[skinidx[model->voxtype[i].skinid]].data.boneid[0]);
  5471                      else
  5472                          for(j = 0; j < M3D_NUMBONE && skin[skinidx[model->voxtype[i].skinid]].data.boneid[j] != M3D_UNDEF &&
  5473                              skin[skinidx[model->voxtype[i].skinid]].data.weight[j] > (M3D_FLOAT)0.0; j++)
  5474                              ptr += sprintf(ptr, " %d:%g", skin[skinidx[model->voxtype[i].skinid]].data.boneid[j],
  5475                                  skin[skinidx[model->voxtype[i].skinid]].data.weight[j]);
  5476                  }
  5477                  if(model->voxtype[i].numitem && model->voxtype[i].item) {
  5478                      for(j = k = 0; j < model->voxtype[i].numitem; j++) {
  5479                          if(!model->voxtype[i].item[j].count || !model->voxtype[i].item[j].name ||
  5480                              !model->voxtype[i].item[j].name[0]) continue;
  5481                          if(!k) { ptr += sprintf(ptr, " {"); k = 1; }
  5482                          sn = _m3d_safestr(model->voxtype[i].item[j].name, 0);
  5483                          if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5484                          ptr += sprintf(ptr, " %d %s", model->voxtype[i].item[j].count, sn);
  5485                          M3D_FREE(sn); sn = NULL;
  5486                      }
  5487                      if(k) ptr += sprintf(ptr, " }");
  5488                  }
  5489                  while(ptr[-1] == '-' || ptr[-1] == ' ') ptr--;
  5490                  ptr += sprintf(ptr, "\r\n");
  5491              }
  5492              ptr += sprintf(ptr, "\r\n");
  5493          }
  5494          if(model->numvoxel && model->voxel && !(flags & M3D_EXP_NOFACE)) {
  5495              for(i = 0; i < model->numvoxel; i++) {
  5496                  ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)128);
  5497                  if(model->voxel[i].name) len += (unsigned int)strlen(model->voxel[i].name);
  5498                  len += model->voxel[i].h * ((model->voxel[i].w * 6 + 2) * model->voxel[i].d + 9);
  5499                  out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5500                  if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5501                  ptr += sprintf(ptr, "Voxel");
  5502                  sn = _m3d_safestr(model->voxel[i].name, 0);
  5503                  if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5504                  if(sn && sn[0])
  5505                      ptr += sprintf(ptr, " %s", sn);
  5506                  M3D_FREE(sn); sn = NULL;
  5507                  ptr += sprintf(ptr, "\r\n");
  5508                  if(model->voxel[i].uncertain)
  5509                      ptr += sprintf(ptr, "uncertain %d %d\r\n", (model->voxel[i].uncertain * 100) / 255, model->voxel[i].groupid);
  5510                  if(model->voxel[i].x || model->voxel[i].y || model->voxel[i].z)
  5511                      ptr += sprintf(ptr, "pos %d %d %d\r\n", model->voxel[i].x, model->voxel[i].y, model->voxel[i].z);
  5512                  ptr += sprintf(ptr, "dim %d %d %d\r\n", model->voxel[i].w, model->voxel[i].h, model->voxel[i].d);
  5513                  for(j = n = 0; j < model->voxel[i].h; j++) {
  5514                      ptr += sprintf(ptr, "layer\r\n");
  5515                      for(k = 0; k < model->voxel[i].d; k++) {
  5516                          for(l = 0; l < model->voxel[i].w; l++, n++) {
  5517                              switch(model->voxel[i].data[n]) {
  5518                                  case M3D_VOXCLEAR: *ptr++ = '-'; break;
  5519                                  case M3D_VOXUNDEF: *ptr++ = '.'; break;
  5520                                  default: ptr += sprintf(ptr, "%d", model->voxel[i].data[n]); break;
  5521                              }
  5522                              *ptr++ = ' ';
  5523                          }
  5524                          ptr--;
  5525                          ptr += sprintf(ptr, "\r\n");
  5526                      }
  5527                  }
  5528                  ptr += sprintf(ptr, "\r\n");
  5529              }
  5530          }
  5531          /* mathematical shapes face */
  5532          if(model->numshape && model->numshape && !(flags & M3D_EXP_NOFACE)) {
  5533              for(j = 0; j < model->numshape; j++) {
  5534                  sn = _m3d_safestr(model->shape[j].name, 0);
  5535                  if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5536                  ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)33);
  5537                  out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5538                  if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5539                  ptr += sprintf(ptr, "Shape %s\r\n", sn);
  5540                  M3D_FREE(sn); sn = NULL;
  5541                  if(model->shape[j].group != M3D_UNDEF && !(flags & M3D_EXP_NOBONE))
  5542                      ptr += sprintf(ptr, "group %d\r\n", model->shape[j].group);
  5543                  for(i = 0; i < model->shape[j].numcmd; i++) {
  5544                      cmd = &model->shape[j].cmd[i];
  5545                      if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg)
  5546                          continue;
  5547                      cd = &m3d_commandtypes[cmd->type];
  5548                      ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(cd->key) + (uintptr_t)3);
  5549                      for(k = 0; k < cd->p; k++)
  5550                          switch(cd->a[k]) {
  5551                              case m3dcp_mi_t: if(cmd->arg[k] != M3D_NOTDEFINED) { len += (unsigned int)strlen(model->material[cmd->arg[k]].name) + 1; } break;
  5552                              case m3dcp_va_t: len += cmd->arg[k] * (cd->p - k - 1) * 16; k = cd->p; break;
  5553                              default: len += 16; break;
  5554                          }
  5555                      out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5556                      if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5557                      ptr += sprintf(ptr, "%s", cd->key);
  5558                      for(k = n = 0, l = cd->p; k < l; k++) {
  5559                          switch(cd->a[((k - n) % (cd->p - n)) + n]) {
  5560                              case m3dcp_mi_t:
  5561                                  if(cmd->arg[k] != M3D_NOTDEFINED) {
  5562                                      sn = _m3d_safestr(model->material[cmd->arg[k]].name, 0);
  5563                                      if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5564                                      ptr += sprintf(ptr, " %s", sn);
  5565                                      M3D_FREE(sn); sn = NULL;
  5566                                  }
  5567                              break;
  5568                              case m3dcp_vc_t: ptr += sprintf(ptr, " %g", *((float*)&cmd->arg[k])); break;
  5569                              case m3dcp_va_t: ptr += sprintf(ptr, " %d[", cmd->arg[k]);
  5570                                  n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1);
  5571                              break;
  5572                              default: ptr += sprintf(ptr, " %d", cmd->arg[k]); break;
  5573                          }
  5574                      }
  5575                      ptr += sprintf(ptr, "%s\r\n", l > cd->p ? " ]" : "");
  5576                  }
  5577                  ptr += sprintf(ptr, "\r\n");
  5578              }
  5579          }
  5580          /* annotation labels */
  5581          if(model->numlabel && model->label && !(flags & M3D_EXP_NOFACE)) {
  5582              for(i = 0, j = 3, length = NULL; i < model->numlabel; i++) {
  5583                  if(model->label[i].name) j += (unsigned int)strlen(model->label[i].name);
  5584                  if(model->label[i].lang) j += (unsigned int)strlen(model->label[i].lang);
  5585                  if(model->label[i].text) j += (unsigned int)strlen(model->label[i].text);
  5586                  j += 40;
  5587              }
  5588              ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)j);
  5589              out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5590              if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5591              for(i = 0; i < model->numlabel; i++) {
  5592                  if(!i || _m3d_strcmp(sl, model->label[i].lang) || _m3d_strcmp(sn, model->label[i].name)) {
  5593                      sl = model->label[i].lang;
  5594                      sn = model->label[i].name;
  5595                      sd = _m3d_safestr(sn, 0);
  5596                      if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; }
  5597                      if(i) ptr += sprintf(ptr, "\r\n");
  5598                      ptr += sprintf(ptr, "Labels %s\r\n", sd);
  5599                      M3D_FREE(sd); sd = NULL;
  5600                      if(model->label[i].color)
  5601                          ptr += sprintf(ptr, "color #0x%08x\r\n", model->label[i].color);
  5602                      if(sl && *sl) {
  5603                          sd = _m3d_safestr(sl, 0);
  5604                          if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; }
  5605                          ptr += sprintf(ptr, "lang %s\r\n", sd);
  5606                          M3D_FREE(sd); sd = NULL;
  5607                      }
  5608                  }
  5609                  sd = _m3d_safestr(model->label[i].text, 2);
  5610                  if(!sd) { setlocale(LC_NUMERIC, ol); sn = sl = NULL; goto memerr; }
  5611                  ptr += sprintf(ptr, "%d %s\r\n", model->label[i].vertexid, sd);
  5612                  M3D_FREE(sd); sd = NULL;
  5613              }
  5614              ptr += sprintf(ptr, "\r\n");
  5615              sn = sl = NULL;
  5616          }
  5617          /* actions */
  5618          if(model->numaction && model->action && !(flags & M3D_EXP_NOACTION)) {
  5619              for(j = 0; j < model->numaction; j++) {
  5620                  a = &model->action[j];
  5621                  sn = _m3d_safestr(a->name, 0);
  5622                  if(!sn) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5623                  ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)strlen(sn) + (uintptr_t)48);
  5624                  for(i = 0; i < a->numframe; i++)
  5625                      len += a->frame[i].numtransform * 128 + 8;
  5626                  out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5627                  if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5628                  ptr += sprintf(ptr, "Action %d %s\r\n", a->durationmsec, sn);
  5629                  M3D_FREE(sn); sn = NULL;
  5630                  for(i = 0; i < a->numframe; i++) {
  5631                      ptr += sprintf(ptr, "frame %d\r\n", a->frame[i].msec);
  5632                      for(k = 0; k < a->frame[i].numtransform; k++) {
  5633                          ptr += sprintf(ptr, "%d %d %d\r\n", a->frame[i].transform[k].boneid,
  5634                              vrtxidx[a->frame[i].transform[k].pos], vrtxidx[a->frame[i].transform[k].ori]);
  5635                      }
  5636                  }
  5637                  ptr += sprintf(ptr, "\r\n");
  5638              }
  5639          }
  5640          /* inlined assets */
  5641          if(model->numinlined && model->inlined) {
  5642              for(i = j = 0; i < model->numinlined; i++)
  5643                  if(model->inlined[i].name)
  5644                      j += (unsigned int)strlen(model->inlined[i].name) + 6;
  5645              if(j > 0) {
  5646                  ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)j + (uintptr_t)16);
  5647                  out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5648                  if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5649                  ptr += sprintf(ptr, "Assets\r\n");
  5650                  for(i = 0; i < model->numinlined; i++)
  5651                      if(model->inlined[i].name)
  5652                          ptr += sprintf(ptr, "%s%s\r\n", model->inlined[i].name, strrchr(model->inlined[i].name, '.') ? "" : ".png");
  5653                  ptr += sprintf(ptr, "\r\n");
  5654              }
  5655          }
  5656          /* extra info */
  5657          if(model->numextra && (flags & M3D_EXP_EXTRA)) {
  5658              for(i = 0; i < model->numextra; i++) {
  5659                  if(model->extra[i]->length < 9) continue;
  5660                  ptr -= (uintptr_t)out; len = (unsigned int)((uintptr_t)ptr + (uintptr_t)17 + (uintptr_t)(model->extra[i]->length * 3));
  5661                  out = (unsigned char*)M3D_REALLOC(out, len); ptr += (uintptr_t)out;
  5662                  if(!out) { setlocale(LC_NUMERIC, ol); goto memerr; }
  5663                  ptr += sprintf(ptr, "Extra %c%c%c%c\r\n",
  5664                      model->extra[i]->magic[0] > ' ' ? model->extra[i]->magic[0] : '_',
  5665                      model->extra[i]->magic[1] > ' ' ? model->extra[i]->magic[1] : '_',
  5666                      model->extra[i]->magic[2] > ' ' ? model->extra[i]->magic[2] : '_',
  5667                      model->extra[i]->magic[3] > ' ' ? model->extra[i]->magic[3] : '_');
  5668                  for(j = 0; j < model->extra[i]->length; j++)
  5669                      ptr += sprintf(ptr, "%02x ", *((unsigned char *)model->extra + sizeof(m3dchunk_t) + j));
  5670                  ptr--;
  5671                  ptr += sprintf(ptr, "\r\n\r\n");
  5672              }
  5673          }
  5674          setlocale(LC_NUMERIC, ol);
  5675          len = (unsigned int)((uintptr_t)ptr - (uintptr_t)out);
  5676          out = (unsigned char*)M3D_REALLOC(out, len + 1);
  5677          if(!out) goto memerr;
  5678          out[len] = 0;
  5679      } else
  5680  #endif
  5681      {
  5682          /* stricly only use LF (newline) in binary */
  5683          sd = _m3d_safestr(model->desc, 3);
  5684          if(!sd) goto memerr;
  5685          /* header */
  5686          h = (m3dhdr_t*)M3D_MALLOC(sizeof(m3dhdr_t) + strlen(sn) + strlen(sl) + strlen(sa) + strlen(sd) + 4);
  5687          if(!h) goto memerr;
  5688          memcpy((uint8_t*)h, "HEAD", 4);
  5689          h->length = sizeof(m3dhdr_t);
  5690          h->scale = scale;
  5691          i = (unsigned int)strlen(sn); memcpy((uint8_t*)h + h->length, sn, i+1); h->length += i+1; M3D_FREE(sn);
  5692          i = (unsigned int)strlen(sl); memcpy((uint8_t*)h + h->length, sl, i+1); h->length += i+1; M3D_FREE(sl);
  5693          i = (unsigned int)strlen(sa); memcpy((uint8_t*)h + h->length, sa, i+1); h->length += i+1; M3D_FREE(sa);
  5694          i = (unsigned int)strlen(sd); memcpy((uint8_t*)h + h->length, sd, i+1); h->length += i+1; M3D_FREE(sd);
  5695          sn = sl = sa = sd = NULL;
  5696          if(model->inlined)
  5697              for(i = 0; i < model->numinlined; i++) {
  5698                  if(model->inlined[i].name && *model->inlined[i].name && model->inlined[i].length > 0) {
  5699                      str = _m3d_addstr(str, &numstr, model->inlined[i].name);
  5700                      if(!str) goto memerr;
  5701                  }
  5702              }
  5703          if(str)
  5704              for(i = 0; i < numstr; i++) {
  5705                  h = _m3d_addhdr(h, &str[i]);
  5706                  if(!h) goto memerr;
  5707              }
  5708          vc_s = quality == M3D_EXP_INT8? 1 : (quality == M3D_EXP_INT16? 2 : (quality == M3D_EXP_DOUBLE? 8 : 4));
  5709          vi_s = maxvrtx < 254 ? 1 : (maxvrtx < 65534 ? 2 : 4);
  5710          si_s = h->length - 16 < 254 ? 1 : (h->length - 16 < 65534 ? 2 : 4);
  5711          ci_s = !numcmap || !cmap ? 0 : (numcmap < 254 ? 1 : (numcmap < 65534 ? 2 : 4));
  5712          ti_s = !maxtmap || !tmap ? 0 : (maxtmap < 254 ? 1 : (maxtmap < 65534 ? 2 : 4));
  5713          bi_s = !model->numbone || !model->bone || (flags & M3D_EXP_NOBONE)? 0 : (model->numbone < 254 ? 1 :
  5714              (model->numbone < 65534 ? 2 : 4));
  5715          nb_s = maxbone < 2 ? 1 : (maxbone == 2 ? 2 : (maxbone <= 4 ? 4 : 8));
  5716          sk_s = !bi_s || !maxskin || !skin ? 0 : (maxskin < 254 ? 1 : (maxskin < 65534 ? 2 : 4));
  5717          fc_s = maxt < 254 ? 1 : (maxt < 65534 ? 2 : 4);
  5718          hi_s = !model->numshape || !model->shape || (flags & M3D_EXP_NOFACE)? 0 : (model->numshape < 254 ? 1 :
  5719              (model->numshape < 65534 ? 2 : 4));
  5720          fi_s = !model->numface || !model->face || (flags & M3D_EXP_NOFACE)? 0 : (model->numface < 254 ? 1 :
  5721              (model->numface < 65534 ? 2 : 4));
  5722          vd_s = !model->numvoxel || !model->voxel || (flags & M3D_EXP_NOFACE)? 0 : (minvox >= -128 && maxvox <= 127 ? 1 :
  5723              (minvox >= -32768 && maxvox <= 32767 ? 2 : 4));
  5724          vp_s = !model->numvoxtype || !model->voxtype || (flags & M3D_EXP_NOFACE)? 0 : (model->numvoxtype < 254 ? 1 :
  5725              (model->numvoxtype < 65534 ? 2 : 4));
  5726          h->types =  (vc_s == 8 ? (3<<0) : (vc_s == 2 ? (1<<0) : (vc_s == 1 ? (0<<0) : (2<<0)))) |
  5727                      (vi_s == 2 ? (1<<2) : (vi_s == 1 ? (0<<2) : (2<<2))) |
  5728                      (si_s == 2 ? (1<<4) : (si_s == 1 ? (0<<4) : (2<<4))) |
  5729                      (ci_s == 2 ? (1<<6) : (ci_s == 1 ? (0<<6) : (ci_s == 4 ? (2<<6) : (3<<6)))) |
  5730                      (ti_s == 2 ? (1<<8) : (ti_s == 1 ? (0<<8) : (ti_s == 4 ? (2<<8) : (3<<8)))) |
  5731                      (bi_s == 2 ? (1<<10): (bi_s == 1 ? (0<<10): (bi_s == 4 ? (2<<10) : (3<<10)))) |
  5732                      (nb_s == 2 ? (1<<12): (nb_s == 1 ? (0<<12): (2<<12))) |
  5733                      (sk_s == 2 ? (1<<14): (sk_s == 1 ? (0<<14): (sk_s == 4 ? (2<<14) : (3<<14)))) |
  5734                      (fc_s == 2 ? (1<<16): (fc_s == 1 ? (0<<16): (2<<16))) |
  5735                      (hi_s == 2 ? (1<<18): (hi_s == 1 ? (0<<18): (hi_s == 4 ? (2<<18) : (3<<18)))) |
  5736                      (fi_s == 2 ? (1<<20): (fi_s == 1 ? (0<<20): (fi_s == 4 ? (2<<20) : (3<<20)))) |
  5737                      (vd_s == 2 ? (1<<22): (vd_s == 1 ? (0<<22): (vd_s == 4 ? (2<<22) : (3<<22)))) |
  5738                      (vp_s == 2 ? (1<<24): (vp_s == 1 ? (0<<24): (vp_s == 4 ? (2<<24) : (3<<24))));
  5739          len = h->length;
  5740          /* color map */
  5741          if(numcmap && cmap && ci_s < 4 && !(flags & M3D_EXP_NOCMAP)) {
  5742              chunklen = 8 + numcmap * sizeof(uint32_t);
  5743              h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  5744              if(!h) goto memerr;
  5745              memcpy((uint8_t*)h + len, "CMAP", 4);
  5746              *((uint32_t*)((uint8_t*)h + len + 4)) = chunklen;
  5747              memcpy((uint8_t*)h + len + 8, cmap, chunklen - 8);
  5748              len += chunklen;
  5749          } else numcmap = 0;
  5750          /* texture map */
  5751          if(numtmap && tmap && !(flags & M3D_EXP_NOTXTCRD) && !(flags & M3D_EXP_NOFACE)) {
  5752              chunklen = 8 + maxtmap * vc_s * 2;
  5753              h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  5754              if(!h) goto memerr;
  5755              memcpy((uint8_t*)h + len, "TMAP", 4);
  5756              length = (uint32_t*)((uint8_t*)h + len + 4);
  5757              out = (uint8_t*)h + len + 8;
  5758              last = M3D_UNDEF;
  5759              for(i = 0; i < numtmap; i++) {
  5760                  if(tmap[i].newidx == last) continue;
  5761                  last = tmap[i].newidx;
  5762                  switch(vc_s) {
  5763                      case 1: *out++ = (uint8_t)(tmap[i].data.u * 255); *out++ = (uint8_t)(tmap[i].data.v * 255); break;
  5764                      case 2:
  5765                          *((uint16_t*)out) = (uint16_t)(tmap[i].data.u * 65535); out += 2;
  5766                          *((uint16_t*)out) = (uint16_t)(tmap[i].data.v * 65535); out += 2;
  5767                      break;
  5768                      case 4:  *((float*)out) = tmap[i].data.u; out += 4;  *((float*)out) = tmap[i].data.v; out += 4; break;
  5769                      case 8: *((double*)out) = tmap[i].data.u; out += 8; *((double*)out) = tmap[i].data.v; out += 8; break;
  5770                  }
  5771              }
  5772              *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
  5773              out = NULL;
  5774              len += *length;
  5775          }
  5776          /* vertex */
  5777          if(numvrtx && vrtx) {
  5778              chunklen = 8 + maxvrtx * (ci_s + sk_s + 4 * vc_s);
  5779              h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  5780              if(!h) goto memerr;
  5781              memcpy((uint8_t*)h + len, "VRTS", 4);
  5782              length = (uint32_t*)((uint8_t*)h + len + 4);
  5783              out = (uint8_t*)h + len + 8;
  5784              last = M3D_UNDEF;
  5785              for(i = 0; i < numvrtx; i++) {
  5786                  if(vrtx[i].newidx == last) continue;
  5787                  last = vrtx[i].newidx;
  5788                  switch(vc_s) {
  5789                      case 1:
  5790                          *out++ = (int8_t)(vrtx[i].data.x * 127);
  5791                          *out++ = (int8_t)(vrtx[i].data.y * 127);
  5792                          *out++ = (int8_t)(vrtx[i].data.z * 127);
  5793                          *out++ = (int8_t)(vrtx[i].data.w * 127);
  5794                      break;
  5795                      case 2:
  5796                          *((int16_t*)out) = (int16_t)(vrtx[i].data.x * 32767); out += 2;
  5797                          *((int16_t*)out) = (int16_t)(vrtx[i].data.y * 32767); out += 2;
  5798                          *((int16_t*)out) = (int16_t)(vrtx[i].data.z * 32767); out += 2;
  5799                          *((int16_t*)out) = (int16_t)(vrtx[i].data.w * 32767); out += 2;
  5800                      break;
  5801                      case 4:
  5802                          *((float*)out) = vrtx[i].data.x; out += 4;
  5803                          *((float*)out) = vrtx[i].data.y; out += 4;
  5804                          *((float*)out) = vrtx[i].data.z; out += 4;
  5805                          *((float*)out) = vrtx[i].data.w; out += 4;
  5806                      break;
  5807                      case 8:
  5808                          *((double*)out) = vrtx[i].data.x; out += 8;
  5809                          *((double*)out) = vrtx[i].data.y; out += 8;
  5810                          *((double*)out) = vrtx[i].data.z; out += 8;
  5811                          *((double*)out) = vrtx[i].data.w; out += 8;
  5812                      break;
  5813                  }
  5814                  idx = _m3d_cmapidx(cmap, numcmap, vrtx[i].data.color);
  5815                  switch(ci_s) {
  5816                      case 1: *out++ = (uint8_t)(idx); break;
  5817                      case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break;
  5818                      case 4: *((uint32_t*)out) = vrtx[i].data.color; out += 4; break;
  5819                  }
  5820                  out = _m3d_addidx(out, sk_s, vrtx[i].data.skinid);
  5821              }
  5822              *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
  5823              out = NULL;
  5824              len += *length;
  5825          }
  5826          /* bones chunk */
  5827          if(model->numbone && model->bone && !(flags & M3D_EXP_NOBONE)) {
  5828              i = 8 + bi_s + sk_s + model->numbone * (bi_s + si_s + 2*vi_s);
  5829              chunklen = i + numskin * nb_s * (bi_s + 1);
  5830              h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  5831              if(!h) goto memerr;
  5832              memcpy((uint8_t*)h + len, "BONE", 4);
  5833              length = (uint32_t*)((uint8_t*)h + len + 4);
  5834              out = (uint8_t*)h + len + 8;
  5835              out = _m3d_addidx(out, bi_s, model->numbone);
  5836              out = _m3d_addidx(out, sk_s, maxskin);
  5837              for(i = 0; i < model->numbone; i++) {
  5838                  out = _m3d_addidx(out, bi_s, model->bone[i].parent);
  5839                  out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->bone[i].name));
  5840                  out = _m3d_addidx(out, vi_s, vrtxidx[model->bone[i].pos]);
  5841                  out = _m3d_addidx(out, vi_s, vrtxidx[model->bone[i].ori]);
  5842              }
  5843              if(numskin && skin && sk_s) {
  5844                  last = M3D_UNDEF;
  5845                  for(i = 0; i < numskin; i++) {
  5846                      if(skin[i].newidx == last) continue;
  5847                      last = skin[i].newidx;
  5848                      memset(&weights, 0, nb_s);
  5849                      for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != M3D_UNDEF &&
  5850                          skin[i].data.weight[j] > (M3D_FLOAT)0.0; j++)
  5851                              weights[j] = (uint8_t)(skin[i].data.weight[j] * 255);
  5852                      switch(nb_s) {
  5853                          case 1: weights[0] = 255; break;
  5854                          case 2: memcpy(out, weights, 2); out += 2; break;
  5855                          case 4: memcpy(out, weights, 4); out += 4; break;
  5856                          case 8: memcpy(out, weights, 8); out += 8; break;
  5857                      }
  5858                      for(j = 0; j < (uint32_t)nb_s && skin[i].data.boneid[j] != M3D_UNDEF && weights[j]; j++) {
  5859                          out = _m3d_addidx(out, bi_s, skin[i].data.boneid[j]);
  5860                          *length += bi_s;
  5861                      }
  5862                  }
  5863              }
  5864              *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
  5865              out = NULL;
  5866              len += *length;
  5867          }
  5868          /* materials */
  5869          if(model->nummaterial && !(flags & M3D_EXP_NOMATERIAL)) {
  5870              for(j = 0; j < model->nummaterial; j++) {
  5871                  if(mtrlidx[j] == M3D_UNDEF || !model->material[j].numprop || !model->material[j].prop) continue;
  5872                  m = &model->material[j];
  5873                  chunklen = 12 + si_s + m->numprop * 5;
  5874                  h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  5875                  if(!h) goto memerr;
  5876                  memcpy((uint8_t*)h + len, "MTRL", 4);
  5877                  length = (uint32_t*)((uint8_t*)h + len + 4);
  5878                  out = (uint8_t*)h + len + 8;
  5879                  out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, m->name));
  5880                  for(i = 0; i < m->numprop; i++) {
  5881                      if(m->prop[i].type >= 128) {
  5882                          if(m->prop[i].value.textureid >= model->numtexture ||
  5883                              !model->texture[m->prop[i].value.textureid].name) continue;
  5884                          k = m3dpf_map;
  5885                      } else {
  5886                          for(k = 256, l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++)
  5887                              if(m->prop[i].type == m3d_propertytypes[l].id) { k = m3d_propertytypes[l].format; break; }
  5888                      }
  5889                      if(k == 256) continue;
  5890                      *out++ = m->prop[i].type;
  5891                      switch(k) {
  5892                          case m3dpf_color:
  5893                              if(!(flags & M3D_EXP_NOCMAP)) {
  5894                                  idx = _m3d_cmapidx(cmap, numcmap, m->prop[i].value.color);
  5895                                  switch(ci_s) {
  5896                                      case 1: *out++ = (uint8_t)(idx); break;
  5897                                      case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break;
  5898                                      case 4: *((uint32_t*)out) = (uint32_t)(m->prop[i].value.color); out += 4; break;
  5899                                  }
  5900                              } else out--;
  5901                          break;
  5902                          case m3dpf_uint8:  *out++ = m->prop[i].value.num; break;
  5903                          case m3dpf_uint16: *((uint16_t*)out) = m->prop[i].value.num; out += 2; break;
  5904                          case m3dpf_uint32: *((uint32_t*)out) = m->prop[i].value.num; out += 4; break;
  5905                          case m3dpf_float:  *((float*)out) = m->prop[i].value.fnum; out += 4; break;
  5906  
  5907                          case m3dpf_map:
  5908                              idx = _m3d_stridx(str, numstr, model->texture[m->prop[i].value.textureid].name);
  5909                              out = _m3d_addidx(out, si_s, idx);
  5910                          break;
  5911                      }
  5912                  }
  5913                  *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
  5914                  len += *length;
  5915                  out = NULL;
  5916              }
  5917          }
  5918          /* procedural face */
  5919          if(model->numinlined && model->inlined && !(flags & M3D_EXP_NOFACE)) {
  5920              /* all inlined assets which are not textures should be procedural surfaces */
  5921              for(j = 0; j < model->numinlined; j++) {
  5922                  if(!model->inlined[j].name || !model->inlined[j].name[0] || model->inlined[j].length < 4 ||
  5923                      !model->inlined[j].data || (model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' &&
  5924                      model->inlined[j].data[3] == 'G'))
  5925                      continue;
  5926                  for(i = k = 0; i < model->numtexture; i++) {
  5927                      if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; }
  5928                  }
  5929                  if(k) continue;
  5930                  numproc++;
  5931                  chunklen = 8 + si_s;
  5932                  h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  5933                  if(!h) goto memerr;
  5934                  memcpy((uint8_t*)h + len, "PROC", 4);
  5935                  *((uint32_t*)((uint8_t*)h + len + 4)) = chunklen;
  5936                  out = (uint8_t*)h + len + 8;
  5937                  out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->inlined[j].name));
  5938                  out = NULL;
  5939                  len += chunklen;
  5940              }
  5941          }
  5942          /* mesh face */
  5943          if(model->numface && face && !(flags & M3D_EXP_NOFACE)) {
  5944              chunklen = 8 + si_s + model->numface * (6 * vi_s + 3 * ti_s + si_s + 1);
  5945              h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  5946              if(!h) goto memerr;
  5947              memcpy((uint8_t*)h + len, "MESH", 4);
  5948              length = (uint32_t*)((uint8_t*)h + len + 4);
  5949              out = (uint8_t*)h + len + 8;
  5950              last = M3D_UNDEF;
  5951  #ifdef M3D_VERTEXMAX
  5952              lastp = M3D_UNDEF;
  5953  #endif
  5954              for(i = 0; i < model->numface; i++) {
  5955                  if(!(flags & M3D_EXP_NOMATERIAL) && face[i].data.materialid != last) {
  5956                      last = face[i].data.materialid;
  5957                      idx = last < model->nummaterial ? _m3d_stridx(str, numstr, model->material[last].name) : 0;
  5958                      *out++ = 0;
  5959                      out = _m3d_addidx(out, si_s, idx);
  5960                  }
  5961  #ifdef M3D_VERTEXMAX
  5962                  if(!(flags & M3D_EXP_NOVRTMAX) && face[i].data.paramid != lastp) {
  5963                      lastp = face[i].data.paramid;
  5964                      idx = lastp < model->numparam ? _m3d_stridx(str, numstr, model->param[lastp].name) : 0;
  5965                      *out++ = 0;
  5966                      out = _m3d_addidx(out, si_s, idx);
  5967                  }
  5968  #endif
  5969                  /* hardcoded triangles. */
  5970                  k = (3 << 4) |
  5971                      (((flags & M3D_EXP_NOTXTCRD) || !ti_s || face[i].data.texcoord[0] == M3D_UNDEF ||
  5972                      face[i].data.texcoord[1] == M3D_UNDEF || face[i].data.texcoord[2] == M3D_UNDEF) ? 0 : 1) |
  5973                      (((flags & M3D_EXP_NONORMAL) || face[i].data.normal[0] == M3D_UNDEF ||
  5974                      face[i].data.normal[1] == M3D_UNDEF || face[i].data.normal[2] == M3D_UNDEF) ? 0 : 2)
  5975  #ifdef M3D_VERTEXMAX
  5976                      | (((flags & M3D_EXP_NOVRTMAX) || face[i].data.vertmax[0] == M3D_UNDEF ||
  5977                      face[i].data.vertmax[1] == M3D_UNDEF || face[i].data.vertmax[2] == M3D_UNDEF) ? 0 : 4)
  5978  #endif
  5979                      ;
  5980                  *out++ = k;
  5981                  for(j = 0; j < 3; j++) {
  5982                      out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.vertex[j]]);
  5983                      if(k & 1)
  5984                          out = _m3d_addidx(out, ti_s, tmapidx[face[i].data.texcoord[j]]);
  5985                      if(k & 2)
  5986                          out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.normal[j]]);
  5987  #ifdef M3D_VERTEXMAX
  5988                      if(k & 4)
  5989                          out = _m3d_addidx(out, vi_s, vrtxidx[face[i].data.vertmax[j]]);
  5990  #endif
  5991                  }
  5992              }
  5993              *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
  5994              len += *length;
  5995              out = NULL;
  5996          }
  5997          /* voxel face */
  5998          if(model->numvoxtype && model->voxtype && !(flags & M3D_EXP_NOFACE)) {
  5999              chunklen = 8 + si_s + model->numvoxtype * (ci_s + si_s + 3 + sk_s);
  6000              for(i = 0; i < model->numvoxtype; i++)
  6001                  chunklen += model->voxtype[i].numitem * (2 + si_s);
  6002              h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  6003              if(!h) goto memerr;
  6004              memcpy((uint8_t*)h + len, "VOXT", 4);
  6005              length = (uint32_t*)((uint8_t*)h + len + 4);
  6006              out = (uint8_t*)h + len + 8;
  6007              for(i = 0; i < model->numvoxtype; i++) {
  6008                  if(!(flags & M3D_EXP_NOCMAP)) {
  6009                      idx = _m3d_cmapidx(cmap, numcmap, model->voxtype[i].color);
  6010                      switch(ci_s) {
  6011                          case 1: *out++ = (uint8_t)(idx); break;
  6012                          case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break;
  6013                          case 4: *((uint32_t*)out) = (uint32_t)(model->voxtype[i].color); out += 4; break;
  6014                      }
  6015                  }
  6016                  out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxtype[i].name));
  6017                  *out++ = (model->voxtype[i].rotation & 0xBF) | (((model->voxtype[i].voxshape >> 8) & 1) << 6);
  6018                  *out++ = model->voxtype[i].voxshape;
  6019                  *out++ = model->voxtype[i].numitem;
  6020                  if(!(flags & M3D_EXP_NOBONE) && model->numbone && maxskin)
  6021                      out = _m3d_addidx(out, sk_s, skinidx[model->voxtype[i].skinid]);
  6022                  for(j = 0; j < model->voxtype[i].numitem; j++) {
  6023                      out = _m3d_addidx(out, 2, model->voxtype[i].item[j].count);
  6024                      out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxtype[i].item[j].name));
  6025                  }
  6026              }
  6027              *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
  6028              len += *length;
  6029              out = NULL;
  6030          }
  6031          if(model->numvoxel && model->voxel && !(flags & M3D_EXP_NOFACE)) {
  6032              for(j = 0; j < model->numvoxel; j++) {
  6033                  chunklen = 8 + si_s + 6 * vd_s + 2 + model->voxel[j].w * model->voxel[j].h * model->voxel[j].d * 3;
  6034                  h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  6035                  if(!h) goto memerr;
  6036                  memcpy((uint8_t*)h + len, "VOXD", 4);
  6037                  length = (uint32_t*)((uint8_t*)h + len + 4);
  6038                  out = (uint8_t*)h + len + 8;
  6039                  out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->voxel[j].name));
  6040                  out = _m3d_addidx(out, vd_s, model->voxel[j].x);
  6041                  out = _m3d_addidx(out, vd_s, model->voxel[j].y);
  6042                  out = _m3d_addidx(out, vd_s, model->voxel[j].z);
  6043                  out = _m3d_addidx(out, vd_s, model->voxel[j].w);
  6044                  out = _m3d_addidx(out, vd_s, model->voxel[j].h);
  6045                  out = _m3d_addidx(out, vd_s, model->voxel[j].d);
  6046                  *out++ = model->voxel[j].uncertain;
  6047                  *out++ = model->voxel[j].groupid;
  6048                  /* RLE compress voxel data */
  6049                  n = model->voxel[j].w * model->voxel[j].h * model->voxel[j].d;
  6050                  k = o = 0; out[o++] = 0;
  6051                  for(i = 0; i < n; i++) {
  6052                      for(l = 1; l < 128 && i + l < n && model->voxel[j].data[i] == model->voxel[j].data[i + l]; l++);
  6053                      if(l > 1) {
  6054                          l--;
  6055                          if(out[k]) { out[k]--; out[o++] = 0x80 | l; }
  6056                          else out[k] = 0x80 | l;
  6057                          switch(vp_s) {
  6058                              case 1: out[o++] = model->voxel[j].data[i]; break;
  6059                              default: *((uint16_t*)(out + o)) = model->voxel[j].data[i]; o += 2; break;
  6060                          }
  6061                          k = o; out[o++] = 0;
  6062                          i += l;
  6063                          continue;
  6064                      }
  6065                      out[k]++;
  6066                      switch(vp_s) {
  6067                          case 1: out[o++] = model->voxel[j].data[i]; break;
  6068                          default: *((uint16_t*)(out + o)) = model->voxel[j].data[i]; o += 2; break;
  6069                      }
  6070                      if(out[k] > 127) { out[k]--; k = o; out[o++] = 0; }
  6071                  }
  6072                  if(!(out[k] & 0x80)) { if(out[k]) out[k]--; else o--; }
  6073                  *length = (uint32_t)((uintptr_t)out + (uintptr_t)o - (uintptr_t)((uint8_t*)h + len));
  6074                  len += *length;
  6075                  out = NULL;
  6076              }
  6077          }
  6078          /* mathematical shapes face */
  6079          if(model->numshape && model->shape && !(flags & M3D_EXP_NOFACE)) {
  6080              for(j = 0; j < model->numshape; j++) {
  6081                  chunklen = 12 + si_s + model->shape[j].numcmd * (M3D_CMDMAXARG + 1) * 4;
  6082                  h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  6083                  if(!h) goto memerr;
  6084                  memcpy((uint8_t*)h + len, "SHPE", 4);
  6085                  length = (uint32_t*)((uint8_t*)h + len + 4);
  6086                  out = (uint8_t*)h + len + 8;
  6087                  out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->shape[j].name));
  6088                  out = _m3d_addidx(out, bi_s, model->shape[j].group);
  6089                  for(i = 0; i < model->shape[j].numcmd; i++) {
  6090                      cmd = &model->shape[j].cmd[i];
  6091                      if(cmd->type >= (unsigned int)(sizeof(m3d_commandtypes)/sizeof(m3d_commandtypes[0])) || !cmd->arg)
  6092                          continue;
  6093                      cd = &m3d_commandtypes[cmd->type];
  6094                      *out++ = (cmd->type & 0x7F) | (cmd->type > 127 ? 0x80 : 0);
  6095                      if(cmd->type > 127) *out++ = (cmd->type >> 7) & 0xff;
  6096                      for(k = n = 0, l = cd->p; k < l; k++) {
  6097                          switch(cd->a[((k - n) % (cd->p - n)) + n]) {
  6098                              case m3dcp_mi_t:
  6099                                  out = _m3d_addidx(out, si_s, cmd->arg[k] < model->nummaterial ?
  6100                                      _m3d_stridx(str, numstr, model->material[cmd->arg[k]].name) : 0);
  6101                              break;
  6102                              case m3dcp_vc_t:
  6103                                  min_x = *((float*)&cmd->arg[k]);
  6104                                  switch(vc_s) {
  6105                                      case 1: *out++ = (int8_t)(min_x * 127); break;
  6106                                      case 2: *((int16_t*)out) = (int16_t)(min_x * 32767); out += 2; break;
  6107                                      case 4: *((float*)out) = min_x; out += 4; break;
  6108                                      case 8: *((double*)out) = min_x; out += 8; break;
  6109                                  }
  6110                              break;
  6111                              case m3dcp_hi_t: out = _m3d_addidx(out, hi_s, cmd->arg[k]); break;
  6112                              case m3dcp_fi_t: out = _m3d_addidx(out, fi_s, cmd->arg[k]); break;
  6113                              case m3dcp_ti_t: out = _m3d_addidx(out, ti_s, cmd->arg[k]); break;
  6114                              case m3dcp_qi_t:
  6115                              case m3dcp_vi_t: out = _m3d_addidx(out, vi_s, cmd->arg[k]); break;
  6116                              case m3dcp_i1_t: out = _m3d_addidx(out, 1, cmd->arg[k]); break;
  6117                              case m3dcp_i2_t: out = _m3d_addidx(out, 2, cmd->arg[k]); break;
  6118                              case m3dcp_i4_t: out = _m3d_addidx(out, 4, cmd->arg[k]); break;
  6119                              case m3dcp_va_t: out = _m3d_addidx(out, 4, cmd->arg[k]);
  6120                                  n = k + 1; l += (cmd->arg[k] - 1) * (cd->p - k - 1);
  6121                              break;
  6122                          }
  6123                      }
  6124                  }
  6125                  *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
  6126                  len += *length;
  6127                  out = NULL;
  6128              }
  6129          }
  6130          /* annotation labels */
  6131          if(model->numlabel && model->label) {
  6132              for(i = 0, length = NULL; i < model->numlabel; i++) {
  6133                  if(!i || _m3d_strcmp(sl, model->label[i].lang) || _m3d_strcmp(sn, model->label[i].name)) {
  6134                      sl = model->label[i].lang;
  6135                      sn = model->label[i].name;
  6136                      if(length) {
  6137                          *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
  6138                          len += *length;
  6139                      }
  6140                      chunklen = 8 + 2 * si_s + ci_s + model->numlabel * (vi_s + si_s);
  6141                      h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  6142                      if(!h) { sn = NULL; sl = NULL; goto memerr; }
  6143                      memcpy((uint8_t*)h + len, "LBLS", 4);
  6144                      length = (uint32_t*)((uint8_t*)h + len + 4);
  6145                      out = (uint8_t*)h + len + 8;
  6146                      out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].name));
  6147                      out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].lang));
  6148                      idx = _m3d_cmapidx(cmap, numcmap, model->label[i].color);
  6149                      switch(ci_s) {
  6150                          case 1: *out++ = (uint8_t)(idx); break;
  6151                          case 2: *((uint16_t*)out) = (uint16_t)(idx); out += 2; break;
  6152                          case 4: *((uint32_t*)out) = model->label[i].color; out += 4; break;
  6153                      }
  6154                  }
  6155                  out = _m3d_addidx(out, vi_s, vrtxidx[model->label[i].vertexid]);
  6156                  out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->label[l].text));
  6157              }
  6158              if(length) {
  6159                  *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
  6160                  len += *length;
  6161              }
  6162              out = NULL;
  6163              sn = sl = NULL;
  6164          }
  6165          /* actions */
  6166          if(model->numaction && model->action && model->numbone && model->bone && !(flags & M3D_EXP_NOACTION)) {
  6167              for(j = 0; j < model->numaction; j++) {
  6168                  a = &model->action[j];
  6169                  chunklen = 14 + si_s + a->numframe * (4 + fc_s + maxt * (bi_s + 2 * vi_s));
  6170                  h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  6171                  if(!h) goto memerr;
  6172                  memcpy((uint8_t*)h + len, "ACTN", 4);
  6173                  length = (uint32_t*)((uint8_t*)h + len + 4);
  6174                  out = (uint8_t*)h + len + 8;
  6175                  out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, a->name));
  6176                  *((uint16_t*)out) = (uint16_t)(a->numframe); out += 2;
  6177                  *((uint32_t*)out) = (uint32_t)(a->durationmsec); out += 4;
  6178                  for(i = 0; i < a->numframe; i++) {
  6179                      *((uint32_t*)out) = (uint32_t)(a->frame[i].msec); out += 4;
  6180                      out = _m3d_addidx(out, fc_s, a->frame[i].numtransform);
  6181                      for(k = 0; k < a->frame[i].numtransform; k++) {
  6182                          out = _m3d_addidx(out, bi_s, a->frame[i].transform[k].boneid);
  6183                          out = _m3d_addidx(out, vi_s, vrtxidx[a->frame[i].transform[k].pos]);
  6184                          out = _m3d_addidx(out, vi_s, vrtxidx[a->frame[i].transform[k].ori]);
  6185                      }
  6186                  }
  6187                  *length = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t*)h + len));
  6188                  len += *length;
  6189                  out = NULL;
  6190              }
  6191          }
  6192          /* inlined assets */
  6193          if(model->numinlined && model->inlined && (numproc || (flags & M3D_EXP_INLINE))) {
  6194              for(j = 0; j < model->numinlined; j++) {
  6195                  if(!model->inlined[j].name || !model->inlined[j].name[0] || model->inlined[j].length<4 || !model->inlined[j].data)
  6196                      continue;
  6197                  if(!(flags & M3D_EXP_INLINE)) {
  6198                      if(model->inlined[j].data[1] == 'P' && model->inlined[j].data[2] == 'N' && model->inlined[j].data[3] == 'G')
  6199                          continue;
  6200                      for(i = k = 0; i < model->numtexture; i++) {
  6201                          if(!strcmp(model->inlined[j].name, model->texture[i].name)) { k = 1; break; }
  6202                      }
  6203                      if(k) continue;
  6204                  }
  6205                  chunklen = 8 + si_s + model->inlined[j].length;
  6206                  h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  6207                  if(!h) goto memerr;
  6208                  memcpy((uint8_t*)h + len, "ASET", 4);
  6209                  *((uint32_t*)((uint8_t*)h + len + 4)) = chunklen;
  6210                  out = (uint8_t*)h + len + 8;
  6211                  out = _m3d_addidx(out, si_s, _m3d_stridx(str, numstr, model->inlined[j].name));
  6212                  memcpy(out, model->inlined[j].data, model->inlined[j].length);
  6213                  out = NULL;
  6214                  len += chunklen;
  6215              }
  6216          }
  6217          /* extra chunks */
  6218          if(model->numextra && model->extra && (flags & M3D_EXP_EXTRA)) {
  6219              for(j = 0; j < model->numextra; j++) {
  6220                  if(!model->extra[j] || model->extra[j]->length < 8)
  6221                      continue;
  6222                  chunklen = model->extra[j]->length;
  6223                  h = (m3dhdr_t*)M3D_REALLOC(h, len + chunklen);
  6224                  if(!h) goto memerr;
  6225                  memcpy((uint8_t*)h + len, model->extra[j], chunklen);
  6226                  len += chunklen;
  6227              }
  6228          }
  6229          /* add end chunk */
  6230          h = (m3dhdr_t*)M3D_REALLOC(h, len + 4);
  6231          if(!h) goto memerr;
  6232          memcpy((uint8_t*)h + len, "OMD3", 4);
  6233          len += 4;
  6234          /* zlib compress */
  6235          if(!(flags & M3D_EXP_NOZLIB)) {
  6236              M3D_LOG("Deflating chunks");
  6237              z = stbi_zlib_compress((unsigned char *)h, len, (int*)&l, 9);
  6238              if(z && l > 0 && l < len) { len = l; M3D_FREE(h); h = (m3dhdr_t*)z; }
  6239          }
  6240          /* add file header at the begining */
  6241          len += 8;
  6242          out = (unsigned char*)M3D_MALLOC(len);
  6243          if(!out) goto memerr;
  6244          memcpy(out, "3DMO", 4);
  6245          *((uint32_t*)(out + 4)) = len;
  6246          /* preview image chunk, must be the first if exists */
  6247          if(model->preview.data && model->preview.length) {
  6248              chunklen = 8 + model->preview.length;
  6249              out = (unsigned char*)M3D_REALLOC(out, len + chunklen);
  6250              if(!out) goto memerr;
  6251              memcpy((uint8_t*)out + 8, "PRVW", 4);
  6252              *((uint32_t*)((uint8_t*)out + 8 + 4)) = chunklen;
  6253              memcpy((uint8_t*)out + 8 + 8, model->preview.data, model->preview.length);
  6254              *((uint32_t*)(out + 4)) += chunklen;
  6255          } else
  6256              chunklen = 0;
  6257          memcpy(out + 8 + chunklen, h, len - 8);
  6258      }
  6259      if(size) *size = out ? len : 0;
  6260      if(vrtxidx) M3D_FREE(vrtxidx);
  6261      if(mtrlidx) M3D_FREE(mtrlidx);
  6262      if(tmapidx) M3D_FREE(tmapidx);
  6263      if(skinidx) M3D_FREE(skinidx);
  6264      if(norm) M3D_FREE(norm);
  6265      if(face) M3D_FREE(face);
  6266      if(cmap) M3D_FREE(cmap);
  6267      if(tmap) M3D_FREE(tmap);
  6268      if(skin) M3D_FREE(skin);
  6269      if(str) M3D_FREE(str);
  6270      if(vrtx) M3D_FREE(vrtx);
  6271      if(h) M3D_FREE(h);
  6272      return out;
  6273  }
  6274  #endif
  6275  
  6276  #endif
  6277  
  6278  #ifdef  __cplusplus
  6279  }
  6280  #ifdef M3D_CPPWRAPPER
  6281  #include <vector>
  6282  #include <string>
  6283  #include <memory>
  6284  
  6285  /*** C++ wrapper class ***/
  6286  namespace M3D {
  6287  #ifdef M3D_IMPLEMENTATION
  6288  
  6289      class Model {
  6290          public:
  6291              m3d_t *model;
  6292  
  6293          public:
  6294              Model() {
  6295                  this->model = (m3d_t*)malloc(sizeof(m3d_t)); memset(this->model, 0, sizeof(m3d_t));
  6296              }
  6297              Model(_unused const std::string &data, _unused m3dread_t ReadFileCB,
  6298                  _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) {
  6299  #ifndef M3D_NOIMPORTER
  6300                  this->model = m3d_load((unsigned char *)data.data(), ReadFileCB, FreeCB, mtllib.model);
  6301  #else
  6302                  Model();
  6303  #endif
  6304              }
  6305              Model(_unused const std::vector<unsigned char> data, _unused m3dread_t ReadFileCB,
  6306                  _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) {
  6307  #ifndef M3D_NOIMPORTER
  6308                  this->model = m3d_load((unsigned char *)&data[0], ReadFileCB, FreeCB, mtllib.model);
  6309  #else
  6310                  Model();
  6311  #endif
  6312              }
  6313              Model(_unused const unsigned char *data, _unused m3dread_t ReadFileCB,
  6314                  _unused m3dfree_t FreeCB, _unused M3D::Model mtllib) {
  6315  #ifndef M3D_NOIMPORTER
  6316                  this->model = m3d_load((unsigned char*)data, ReadFileCB, FreeCB, mtllib.model);
  6317  #else
  6318                  Model();
  6319  #endif
  6320              }
  6321              ~Model() { m3d_free(this->model); }
  6322  
  6323          public:
  6324              m3d_t *getCStruct() { return this->model; }
  6325              std::string getName() { return std::string(this->model->name); }
  6326              void setName(std::string name) { this->model->name = (char*)name.c_str(); }
  6327              std::string getLicense() { return std::string(this->model->license); }
  6328              void setLicense(std::string license) { this->model->license = (char*)license.c_str(); }
  6329              std::string getAuthor() { return std::string(this->model->author); }
  6330              void setAuthor(std::string author) { this->model->author = (char*)author.c_str(); }
  6331              std::string getDescription() { return std::string(this->model->desc); }
  6332              void setDescription(std::string desc) { this->model->desc = (char*)desc.c_str(); }
  6333              float getScale() { return this->model->scale; }
  6334              void setScale(float scale) { this->model->scale = scale; }
  6335              std::vector<unsigned char> getPreview() { return this->model->preview.data ?
  6336                  std::vector<unsigned char>(this->model->preview.data, this->model->preview.data + this->model->preview.length) :
  6337                  std::vector<unsigned char>(); }
  6338              std::vector<uint32_t> getColorMap() { return this->model->cmap ? std::vector<uint32_t>(this->model->cmap,
  6339                  this->model->cmap + this->model->numcmap) : std::vector<uint32_t>(); }
  6340              std::vector<m3dti_t> getTextureMap() { return this->model->tmap ? std::vector<m3dti_t>(this->model->tmap,
  6341                  this->model->tmap + this->model->numtmap) : std::vector<m3dti_t>(); }
  6342              std::vector<m3dtx_t> getTextures() { return this->model->texture ? std::vector<m3dtx_t>(this->model->texture,
  6343                  this->model->texture + this->model->numtexture) : std::vector<m3dtx_t>(); }
  6344              std::string getTextureName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numtexture ?
  6345                  std::string(this->model->texture[idx].name) : nullptr; }
  6346              std::vector<m3db_t> getBones() { return this->model->bone ? std::vector<m3db_t>(this->model->bone, this->model->bone +
  6347                  this->model->numbone) : std::vector<m3db_t>(); }
  6348              std::string getBoneName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numbone ?
  6349                  std::string(this->model->bone[idx].name) : nullptr; }
  6350              std::vector<m3dm_t> getMaterials() { return this->model->material ? std::vector<m3dm_t>(this->model->material,
  6351                  this->model->material + this->model->nummaterial) : std::vector<m3dm_t>(); }
  6352              std::string getMaterialName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->nummaterial ?
  6353                  std::string(this->model->material[idx].name) : nullptr; }
  6354              int getMaterialPropertyInt(int idx, int type) {
  6355                      if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 0 || type >= 127 ||
  6356                          !this->model->material[idx].prop) return -1;
  6357                      for (int i = 0; i < this->model->material[idx].numprop; i++) {
  6358                          if (this->model->material[idx].prop[i].type == type)
  6359                              return this->model->material[idx].prop[i].value.num;
  6360                      }
  6361                      return -1;
  6362                  }
  6363              uint32_t getMaterialPropertyColor(int idx, int type) { return this->getMaterialPropertyInt(idx, type); }
  6364              float getMaterialPropertyFloat(int idx, int type) {
  6365                      if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 0 || type >= 127 ||
  6366                          !this->model->material[idx].prop) return -1.0f;
  6367                      for (int i = 0; i < this->model->material[idx].numprop; i++) {
  6368                          if (this->model->material[idx].prop[i].type == type)
  6369                              return this->model->material[idx].prop[i].value.fnum;
  6370                      }
  6371                      return -1.0f;
  6372                  }
  6373              m3dtx_t* getMaterialPropertyMap(int idx, int type) {
  6374                      if (idx < 0 || (unsigned int)idx >= this->model->nummaterial || type < 128 || type > 255 ||
  6375                          !this->model->material[idx].prop) return nullptr;
  6376                      for (int i = 0; i < this->model->material[idx].numprop; i++) {
  6377                          if (this->model->material[idx].prop[i].type == type)
  6378                              return this->model->material[idx].prop[i].value.textureid < this->model->numtexture ?
  6379                                  &this->model->texture[this->model->material[idx].prop[i].value.textureid] : nullptr;
  6380                      }
  6381                      return nullptr;
  6382                  }
  6383              std::vector<m3dv_t> getVertices() { return this->model->vertex ? std::vector<m3dv_t>(this->model->vertex,
  6384                  this->model->vertex + this->model->numvertex) : std::vector<m3dv_t>(); }
  6385              std::vector<m3df_t> getFace() { return this->model->face ? std::vector<m3df_t>(this->model->face, this->model->face +
  6386                  this->model->numface) : std::vector<m3df_t>(); }
  6387              std::vector<m3dvt_t> getVoxelTypes() { return this->model->voxtype ? std::vector<m3dvt_t>(this->model->voxtype,
  6388                  this->model->voxtype + this->model->numvoxtype) : std::vector<m3dvt_t>(); }
  6389              std::string getVoxelTypeName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxtype &&
  6390                  this->model->voxtype[idx].name && this->model->voxtype[idx].name[0] ?
  6391                  std::string(this->model->voxtype[idx].name) : nullptr; }
  6392              std::vector<m3dvi_t> getVoxelTypeItems(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxtype &&
  6393                  this->model->voxtype[idx].item ? std::vector<m3dvi_t>(this->model->voxtype[idx].item,
  6394                  this->model->voxtype[idx].item + this->model->voxtype[idx].numitem) : std::vector<m3dvi_t>(); }
  6395              std::vector<m3dvx_t> getVoxelBlocks() { return this->model->voxel ? std::vector<m3dvx_t>(this->model->voxel,
  6396                  this->model->voxel + this->model->numvoxel) : std::vector<m3dvx_t>(); }
  6397              std::string getVoxelBlockName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxel &&
  6398                  this->model->voxel[idx].name && this->model->voxel[idx].name[0] ?
  6399                  std::string(this->model->voxel[idx].name) : nullptr; }
  6400              std::vector<M3D_VOXEL> getVoxelBlockData(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numvoxel &&
  6401                  this->model->voxel[idx].data ? std::vector<M3D_VOXEL>(this->model->voxel[idx].data,
  6402                  this->model->voxel[idx].data + this->model->voxel[idx].w*this->model->voxel[idx].h*this->model->voxel[idx].d) :
  6403                  std::vector<M3D_VOXEL>(); }
  6404              std::vector<m3dh_t> getShape() { return this->model->shape ? std::vector<m3dh_t>(this->model->shape,
  6405                  this->model->shape + this->model->numshape) : std::vector<m3dh_t>(); }
  6406              std::string getShapeName(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape &&
  6407                  this->model->shape[idx].name && this->model->shape[idx].name[0] ?
  6408                  std::string(this->model->shape[idx].name) : nullptr; }
  6409              unsigned int getShapeGroup(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape ?
  6410                  this->model->shape[idx].group : 0xFFFFFFFF; }
  6411              std::vector<m3dc_t> getShapeCommands(int idx) { return idx >= 0 && (unsigned int)idx < this->model->numshape &&
  6412                  this->model->shape[idx].cmd ? std::vector<m3dc_t>(this->model->shape[idx].cmd, this->model->shape[idx].cmd +
  6413                  this->model->shape[idx].numcmd) : std::vector<m3dc_t>(); }
  6414              std::vector<m3dl_t> getAnnotationLabels() { return this->model->label ? std::vector<m3dl_t>(this->model->label,
  6415                  this->model->label + this->model->numlabel) : std::vector<m3dl_t>(); }
  6416              std::vector<m3ds_t> getSkin() { return this->model->skin ? std::vector<m3ds_t>(this->model->skin, this->model->skin +
  6417                  this->model->numskin) : std::vector<m3ds_t>(); }
  6418              std::vector<m3da_t> getActions() { return this->model->action ? std::vector<m3da_t>(this->model->action,
  6419                  this->model->action + this->model->numaction) : std::vector<m3da_t>(); }
  6420              std::string getActionName(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ?
  6421                  std::string(this->model->action[aidx].name) : nullptr; }
  6422              unsigned int getActionDuration(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ?
  6423                  this->model->action[aidx].durationmsec : 0; }
  6424              std::vector<m3dfr_t> getActionFrames(int aidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction ?
  6425                  std::vector<m3dfr_t>(this->model->action[aidx].frame, this->model->action[aidx].frame +
  6426                  this->model->action[aidx].numframe) : std::vector<m3dfr_t>(); }
  6427              unsigned int getActionFrameTimestamp(int aidx, int fidx) { return aidx >= 0 && (unsigned int)aidx < this->model->numaction?
  6428                      (fidx >= 0 && (unsigned int)fidx < this->model->action[aidx].numframe ?
  6429                      this->model->action[aidx].frame[fidx].msec : 0) : 0; }
  6430              std::vector<m3dtr_t> getActionFrameTransforms(int aidx, int fidx) {
  6431                  return aidx >= 0 && (unsigned int)aidx < this->model->numaction ? (
  6432                      fidx >= 0 && (unsigned int)fidx < this->model->action[aidx].numframe ?
  6433                      std::vector<m3dtr_t>(this->model->action[aidx].frame[fidx].transform,
  6434                      this->model->action[aidx].frame[fidx].transform + this->model->action[aidx].frame[fidx].numtransform) :
  6435                      std::vector<m3dtr_t>()) : std::vector<m3dtr_t>(); }
  6436              std::vector<m3dtr_t> getActionFrame(int aidx, int fidx, std::vector<m3dtr_t> skeleton) {
  6437                  m3dtr_t *pose = m3d_frame(this->model, (unsigned int)aidx, (unsigned int)fidx,
  6438                      skeleton.size() ? &skeleton[0] : nullptr);
  6439                  return std::vector<m3dtr_t>(pose, pose + this->model->numbone); }
  6440              std::vector<m3db_t> getActionPose(int aidx, unsigned int msec) {
  6441                  m3db_t *pose = m3d_pose(this->model, (unsigned int)aidx, (unsigned int)msec);
  6442                  return std::vector<m3db_t>(pose, pose + this->model->numbone); }
  6443              std::vector<m3di_t> getInlinedAssets() { return this->model->inlined ? std::vector<m3di_t>(this->model->inlined,
  6444                  this->model->inlined + this->model->numinlined) : std::vector<m3di_t>(); }
  6445              std::vector<std::unique_ptr<m3dchunk_t>> getExtras() { return this->model->extra ?
  6446                  std::vector<std::unique_ptr<m3dchunk_t>>(this->model->extra,
  6447                  this->model->extra + this->model->numextra) : std::vector<std::unique_ptr<m3dchunk_t>>(); }
  6448              std::vector<unsigned char> Save(_unused int quality, _unused int flags) {
  6449  #ifdef M3D_EXPORTER
  6450                  unsigned int size;
  6451                  unsigned char *ptr = m3d_save(this->model, quality, flags, &size);
  6452                  return ptr && size ? std::vector<unsigned char>(ptr, ptr + size) : std::vector<unsigned char>();
  6453  #else
  6454                  return std::vector<unsigned char>();
  6455  #endif
  6456              }
  6457      };
  6458  
  6459  #else
  6460      class Model {
  6461          private:
  6462              m3d_t *model;
  6463  
  6464          public:
  6465              Model(const std::string &data, m3dread_t ReadFileCB, m3dfree_t FreeCB);
  6466              Model(const std::vector<unsigned char> data, m3dread_t ReadFileCB, m3dfree_t FreeCB);
  6467              Model(const unsigned char *data, m3dread_t ReadFileCB, m3dfree_t FreeCB);
  6468              Model();
  6469              ~Model();
  6470  
  6471          public:
  6472              m3d_t *getCStruct();
  6473              std::string getName();
  6474              void setName(std::string name);
  6475              std::string getLicense();
  6476              void setLicense(std::string license);
  6477              std::string getAuthor();
  6478              void setAuthor(std::string author);
  6479              std::string getDescription();
  6480              void setDescription(std::string desc);
  6481              float getScale();
  6482              void setScale(float scale);
  6483              std::vector<unsigned char> getPreview();
  6484              std::vector<uint32_t> getColorMap();
  6485              std::vector<m3dti_t> getTextureMap();
  6486              std::vector<m3dtx_t> getTextures();
  6487              std::string getTextureName(int idx);
  6488              std::vector<m3db_t> getBones();
  6489              std::string getBoneName(int idx);
  6490              std::vector<m3dm_t> getMaterials();
  6491              std::string getMaterialName(int idx);
  6492              int getMaterialPropertyInt(int idx, int type);
  6493              uint32_t getMaterialPropertyColor(int idx, int type);
  6494              float getMaterialPropertyFloat(int idx, int type);
  6495              m3dtx_t* getMaterialPropertyMap(int idx, int type);
  6496              std::vector<m3dv_t> getVertices();
  6497              std::vector<m3df_t> getFace();
  6498              std::vector<m3dvt_t> getVoxelTypes();
  6499              std::string getVoxelTypeName(int idx);
  6500              std::vector<m3dvi_t> getVoxelTypeItems(int idx);
  6501              std::vector<m3dvx_t> getVoxelBlocks();
  6502              std::string getVoxelBlockName(int idx);
  6503              std::vector<M3D_VOXEL> getVoxelBlockData(int idx);
  6504              std::vector<m3dh_t> getShape();
  6505              std::string getShapeName(int idx);
  6506              unsigned int getShapeGroup(int idx);
  6507              std::vector<m3dc_t> getShapeCommands(int idx);
  6508              std::vector<m3dl_t> getAnnotationLabels();
  6509              std::vector<m3ds_t> getSkin();
  6510              std::vector<m3da_t> getActions();
  6511              std::string getActionName(int aidx);
  6512              unsigned int getActionDuration(int aidx);
  6513              std::vector<m3dfr_t> getActionFrames(int aidx);
  6514              unsigned int getActionFrameTimestamp(int aidx, int fidx);
  6515              std::vector<m3dtr_t> getActionFrameTransforms(int aidx, int fidx);
  6516              std::vector<m3dtr_t> getActionFrame(int aidx, int fidx, std::vector<m3dtr_t> skeleton);
  6517              std::vector<m3db_t> getActionPose(int aidx, unsigned int msec);
  6518              std::vector<m3di_t> getInlinedAssets();
  6519              std::vector<std::unique_ptr<m3dchunk_t>> getExtras();
  6520              std::vector<unsigned char> Save(int quality, int flags);
  6521      };
  6522  
  6523  #endif /* impl */
  6524  }
  6525  #endif
  6526  
  6527  #endif /* __cplusplus */
  6528  
  6529  #endif