]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/picomodel/pm_md3.c
Merge commit 'ff48e71434a414958e6e56628ccf04284d030784' into master-merge
[xonotic/netradiant.git] / libs / picomodel / pm_md3.c
index 6d87469d80e796bd1bac7673db1adcaa0e90058c..1b93c7b4d93b2fe875638332fc32510df88a2cda 100644 (file)
-/* -----------------------------------------------------------------------------\r
-\r
-PicoModel Library \r
-\r
-Copyright (c) 2002, Randy Reddig & seaw0lf\r
-All rights reserved.\r
-\r
-Redistribution and use in source and binary forms, with or without modification,\r
-are permitted provided that the following conditions are met:\r
-\r
-Redistributions of source code must retain the above copyright notice, this list\r
-of conditions and the following disclaimer.\r
-\r
-Redistributions in binary form must reproduce the above copyright notice, this\r
-list of conditions and the following disclaimer in the documentation and/or\r
-other materials provided with the distribution.\r
-\r
-Neither the names of the copyright holders nor the names of its contributors may\r
-be used to endorse or promote products derived from this software without\r
-specific prior written permission. \r
-\r
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\r
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\r
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-\r
------------------------------------------------------------------------------ */\r
-\r
-\r
-\r
-/* marker */\r
-#define PM_MD3_C\r
-\r
-\r
-\r
-/* dependencies */\r
-#include "picointernal.h"\r
-\r
-\r
-\r
-/* md3 model format */\r
-#define MD3_MAGIC                      "IDP3"\r
-#define MD3_VERSION                    15\r
-\r
-/* md3 vertex scale */\r
-#define MD3_SCALE                (1.0f / 64.0f)\r
-\r
-/* md3 model frame information */\r
-typedef struct md3Frame_s\r
-{\r
-       float           bounds[ 2 ][ 3 ];\r
-       float           localOrigin[ 3 ];\r
-       float           radius;\r
-       char            creator[ 16 ];\r
-}\r
-md3Frame_t;\r
-\r
-/* md3 model tag information */\r
-typedef struct md3Tag_s\r
-{\r
-       char            name[ 64 ];\r
-       float           origin[ 3 ];\r
-       float           axis[ 3 ][ 3 ];\r
-}\r
-md3Tag_t;\r
-\r
-/* md3 surface md3 (one object mesh) */\r
-typedef struct md3Surface_s\r
-{\r
-       char            magic[ 4 ];\r
-       char            name[ 64 ];             /* polyset name */\r
-       int                     flags;\r
-       int                     numFrames;              /* all model surfaces should have the same */\r
-       int                     numShaders;             /* all model surfaces should have the same */\r
-       int                     numVerts;\r
-       int                     numTriangles;\r
-       int                     ofsTriangles;\r
-       int                     ofsShaders;             /* offset from start of md3Surface_t */\r
-       int                     ofsSt;                  /* texture coords are common for all frames */\r
-       int                     ofsVertexes;    /* numVerts * numFrames */\r
-       int                     ofsEnd;                 /* next surface follows */\r
-}\r
-md3Surface_t;\r
-\r
-typedef struct md3Shader_s\r
-{\r
-       char            name[ 64 ];\r
-       int                     shaderIndex;    /* for ingame use */\r
-}\r
-md3Shader_t;\r
-\r
-typedef struct md3Triangle_s\r
-{\r
-       int                     indexes[ 3 ];\r
-}\r
-md3Triangle_t;\r
-\r
-typedef struct md3TexCoord_s\r
-{\r
-       float           st[ 2 ];\r
-}\r
-md3TexCoord_t;\r
-\r
-typedef struct md3Vertex_s\r
-{\r
-       short           xyz[ 3 ];\r
-       short           normal;\r
-}\r
-md3Vertex_t;\r
-\r
-\r
-/* md3 model file md3 structure */\r
-typedef struct md3_s\r
-{\r
-       char            magic[ 4 ];             /* MD3_MAGIC */\r
-       int                     version;\r
-       char            name[ 64 ];             /* model name */\r
-       int                     flags;\r
-       int                     numFrames;\r
-       int                     numTags;\r
-       int                     numSurfaces;\r
-       int                     numSkins;               /* number of skins for the mesh */\r
-       int                     ofsFrames;              /* offset for first frame */\r
-       int                     ofsTags;                /* numFrames * numTags */\r
-       int                     ofsSurfaces;    /* first surface, others follow */\r
-       int                     ofsEnd;                 /* end of file */\r
-}\r
-md3_t;\r
-\r
-\r
-\r
-\r
-/*\r
-_md3_canload()\r
-validates a quake3 arena md3 model file. btw, i use the\r
-preceding underscore cause it's a static func referenced\r
-by one structure only.\r
-*/\r
-\r
-static int _md3_canload( PM_PARAMS_CANLOAD )\r
-{\r
-       md3_t   *md3;\r
-       \r
-\r
-       /* to keep the compiler happy */\r
-       *fileName = *fileName;\r
-       \r
-       /* sanity check */\r
-       if( bufSize < ( sizeof( *md3 ) * 2) )\r
-               return PICO_PMV_ERROR_SIZE;\r
-       \r
-       /* set as md3 */\r
-       md3     = (md3_t*) buffer;\r
-       \r
-       /* check md3 magic */\r
-       if( *((int*) md3->magic) != *((int*) MD3_MAGIC) ) \r
-               return PICO_PMV_ERROR_IDENT;\r
-       \r
-       /* check md3 version */\r
-       if( _pico_little_long( md3->version ) != MD3_VERSION )\r
-               return PICO_PMV_ERROR_VERSION;\r
-       \r
-       /* file seems to be a valid md3 */\r
-       return PICO_PMV_OK;\r
-}\r
-\r
-\r
-\r
-/*\r
-_md3_load()\r
-loads a quake3 arena md3 model file.\r
-*/\r
-\r
-static picoModel_t *_md3_load( PM_PARAMS_LOAD )\r
-{\r
-       int                             i, j;\r
-       picoByte_t              *bb;\r
-       md3_t                   *md3;\r
-       md3Surface_t    *surface;\r
-       md3Shader_t             *shader;\r
-       md3TexCoord_t   *texCoord;\r
-       md3Frame_t              *frame;\r
-       md3Triangle_t   *triangle;\r
-       md3Vertex_t             *vertex;\r
-       double                  lat, lng;\r
-       \r
-       picoModel_t             *picoModel;\r
-       picoSurface_t   *picoSurface;\r
-       picoShader_t    *picoShader;\r
-       picoVec3_t              xyz, normal;\r
-       picoVec2_t              st;\r
-       picoColor_t             color;\r
-       \r
-       \r
-       /* -------------------------------------------------\r
-       md3 loading\r
-       ------------------------------------------------- */\r
-\r
-\r
-       /* set as md3 */\r
-       bb = (picoByte_t*) buffer;\r
-       md3     = (md3_t*) buffer;\r
-       \r
-       /* check ident and version */\r
-       if( *((int*) md3->magic) != *((int*) MD3_MAGIC) || _pico_little_long( md3->version ) != MD3_VERSION )\r
-       {\r
-               /* not an md3 file (todo: set error) */\r
-               return NULL;\r
-       }\r
-       \r
-       /* swap md3; sea: swaps fixed */\r
-       md3->version = _pico_little_long( md3->version );\r
-       md3->numFrames = _pico_little_long( md3->numFrames );\r
-       md3->numTags = _pico_little_long( md3->numTags );\r
-       md3->numSurfaces = _pico_little_long( md3->numSurfaces );\r
-       md3->numSkins = _pico_little_long( md3->numSkins );\r
-       md3->ofsFrames = _pico_little_long( md3->ofsFrames );\r
-       md3->ofsTags = _pico_little_long( md3->ofsTags );\r
-       md3->ofsSurfaces = _pico_little_long( md3->ofsSurfaces );\r
-       md3->ofsEnd = _pico_little_long( md3->ofsEnd );\r
-       \r
-       /* do frame check */\r
-       if( md3->numFrames < 1 )\r
-       {\r
-               _pico_printf( PICO_ERROR, "MD3 with 0 frames" );\r
-               return NULL;\r
-       }\r
-       \r
-       if( frameNum < 0 || frameNum >= md3->numFrames )\r
-       {\r
-               _pico_printf( PICO_ERROR, "Invalid or out-of-range MD3 frame specified" );\r
-               return NULL;\r
-       }\r
-       \r
-       /* swap frames */\r
-       frame = (md3Frame_t*) (bb + md3->ofsFrames );\r
-       for( i = 0; i < md3->numFrames; i++, frame++ )\r
-       {\r
-               frame->radius = _pico_little_float( frame->radius );\r
-               for( j = 0; j < 3; j++ )\r
-               {\r
-                       frame->bounds[ 0 ][ j ] = _pico_little_float( frame->bounds[ 0 ][ j ] );\r
-                       frame->bounds[ 1 ][ j ] = _pico_little_float( frame->bounds[ 1 ][ j ] );\r
-                       frame->localOrigin[ j ] = _pico_little_float( frame->localOrigin[ j ] );\r
-               }\r
-       }\r
-       \r
-       /* swap surfaces */\r
-       surface = (md3Surface_t*) (bb + md3->ofsSurfaces);\r
-       for( i = 0; i < md3->numSurfaces; i++ )\r
-       {\r
-               /* swap surface md3; sea: swaps fixed */\r
-               surface->flags = _pico_little_long( surface->flags );\r
-               surface->numFrames = _pico_little_long( surface->numFrames );\r
-               surface->numShaders = _pico_little_long( surface->numShaders );\r
-               surface->numTriangles = _pico_little_long( surface->numTriangles );\r
-               surface->ofsTriangles = _pico_little_long( surface->ofsTriangles );\r
-               surface->numVerts = _pico_little_long( surface->numVerts );\r
-               surface->ofsShaders = _pico_little_long( surface->ofsShaders );\r
-               surface->ofsSt = _pico_little_long( surface->ofsSt );\r
-               surface->ofsVertexes = _pico_little_long( surface->ofsVertexes );\r
-               surface->ofsEnd = _pico_little_long( surface->ofsEnd );\r
-               \r
-               /* swap triangles */\r
-               triangle = (md3Triangle_t*) ((picoByte_t*) surface + surface->ofsTriangles);\r
-               for( j = 0; j < surface->numTriangles; j++, triangle++ )\r
-               {\r
-                       /* sea: swaps fixed */\r
-                       triangle->indexes[ 0 ] = _pico_little_long( triangle->indexes[ 0 ] );\r
-                       triangle->indexes[ 1 ] = _pico_little_long( triangle->indexes[ 1 ] );\r
-                       triangle->indexes[ 2 ] = _pico_little_long( triangle->indexes[ 2 ] );\r
-               }\r
-               \r
-               /* swap st coords */\r
-               texCoord = (md3TexCoord_t*) ((picoByte_t*) surface + surface->ofsSt);\r
-               for( j = 0; j < surface->numVerts; j++, texCoord++ )\r
-               {\r
-                       texCoord->st[ 0 ] = _pico_little_float( texCoord->st[ 0 ] );\r
-                       texCoord->st[ 1 ] = _pico_little_float( texCoord->st[ 1 ] );\r
-               }\r
-               \r
-               /* swap xyz/normals */\r
-               vertex = (md3Vertex_t*) ((picoByte_t*) surface + surface->ofsVertexes);\r
-               for( j = 0; j < (surface->numVerts * surface->numFrames); j++, vertex++)\r
-               {\r
-                       vertex->xyz[ 0 ] = _pico_little_short( vertex->xyz[ 0 ] );\r
-                       vertex->xyz[ 1 ] = _pico_little_short( vertex->xyz[ 1 ] );\r
-                       vertex->xyz[ 2 ] = _pico_little_short( vertex->xyz[ 2 ] );\r
-                       vertex->normal   = _pico_little_short( vertex->normal );\r
-               }\r
-               \r
-               /* get next surface */\r
-               surface = (md3Surface_t*) ((picoByte_t*) surface + surface->ofsEnd);\r
-       }\r
-       \r
-       /* -------------------------------------------------\r
-       pico model creation\r
-       ------------------------------------------------- */\r
-       \r
-       /* create new pico model */\r
-       picoModel = PicoNewModel();\r
-       if( picoModel == NULL )\r
-       {\r
-               _pico_printf( PICO_ERROR, "Unable to allocate a new model" );\r
-               return NULL;\r
-       }\r
-       \r
-       /* do model setup */\r
-       PicoSetModelFrameNum( picoModel, frameNum );\r
-       PicoSetModelNumFrames( picoModel, md3->numFrames ); /* sea */\r
-       PicoSetModelName( picoModel, fileName );\r
-       PicoSetModelFileName( picoModel, fileName );\r
-       \r
-       /* md3 surfaces become picomodel surfaces */\r
-       surface = (md3Surface_t*) (bb + md3->ofsSurfaces);\r
-       \r
-       /* run through md3 surfaces */\r
-       for( i = 0; i < md3->numSurfaces; i++ )\r
-       {\r
-               /* allocate new pico surface */\r
-               picoSurface = PicoNewSurface( picoModel );\r
-               if( picoSurface == NULL )\r
-               {\r
-                       _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );\r
-                       PicoFreeModel( picoModel ); /* sea */\r
-                       return NULL;\r
-               }\r
-               \r
-               /* md3 model surfaces are all triangle meshes */\r
-               PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );\r
-               \r
-               /* set surface name */\r
-               PicoSetSurfaceName( picoSurface, surface->name );\r
-               \r
-               /* create new pico shader -sea */\r
-               picoShader = PicoNewShader( picoModel );\r
-               if( picoShader == NULL )\r
-               {\r
-                       _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );\r
-                       PicoFreeModel( picoModel );\r
-                       return NULL;\r
-               }\r
-               \r
-               /* detox and set shader name */\r
-               shader = (md3Shader_t*) ((picoByte_t*) surface + surface->ofsShaders);\r
-               _pico_setfext( shader->name, "" );\r
-               _pico_unixify( shader->name );\r
-               PicoSetShaderName( picoShader, shader->name );\r
-               \r
-               /* associate current surface with newly created shader */\r
-               PicoSetSurfaceShader( picoSurface, picoShader );\r
-               \r
-               /* copy indexes */\r
-               triangle = (md3Triangle_t *) ((picoByte_t*) surface + surface->ofsTriangles);\r
-               \r
-               for( j = 0; j < surface->numTriangles; j++, triangle++ )\r
-               {\r
-                       PicoSetSurfaceIndex( picoSurface, (j * 3 + 0), (picoIndex_t) triangle->indexes[ 0 ] );\r
-                       PicoSetSurfaceIndex( picoSurface, (j * 3 + 1), (picoIndex_t) triangle->indexes[ 1 ] );\r
-                       PicoSetSurfaceIndex( picoSurface, (j * 3 + 2), (picoIndex_t) triangle->indexes[ 2 ] );\r
-               }\r
-               \r
-               /* copy vertexes */\r
-               texCoord = (md3TexCoord_t*) ((picoByte_t *) surface + surface->ofsSt);\r
-               vertex = (md3Vertex_t*) ((picoByte_t*) surface + surface->ofsVertexes + surface->numVerts * frameNum * sizeof( md3Vertex_t ) );\r
-               _pico_set_color( color, 255, 255, 255, 255 );\r
-               \r
-               for( j = 0; j < surface->numVerts; j++, texCoord++, vertex++ )\r
-               {\r
-                       /* set vertex origin */\r
-                       xyz[ 0 ] = MD3_SCALE * vertex->xyz[ 0 ];\r
-                       xyz[ 1 ] = MD3_SCALE * vertex->xyz[ 1 ];\r
-                       xyz[ 2 ] = MD3_SCALE * vertex->xyz[ 2 ];\r
-                       PicoSetSurfaceXYZ( picoSurface, j, xyz );\r
-                       \r
-                       /* decode lat/lng normal to 3 float normal */\r
-                       lat = (float) ((vertex->normal >> 8) & 0xff);\r
-                       lng = (float) (vertex->normal & 0xff);\r
-                       lat *= PICO_PI / 128;\r
-                       lng *= PICO_PI / 128;\r
-                       normal[ 0 ] = (picoVec_t) cos( lat ) * (picoVec_t) sin( lng );\r
-                       normal[ 1 ] = (picoVec_t) sin( lat ) * (picoVec_t) sin( lng );\r
-                       normal[ 2 ] = (picoVec_t) cos( lng );\r
-                       PicoSetSurfaceNormal( picoSurface, j, normal );\r
-                       \r
-                       /* set st coords */\r
-                       st[ 0 ] = texCoord->st[ 0 ];\r
-                       st[ 1 ] = texCoord->st[ 1 ];\r
-                       PicoSetSurfaceST( picoSurface, 0, j, st );\r
-\r
-                       /* set color */\r
-                       PicoSetSurfaceColor( picoSurface, 0, j, color );\r
-               }\r
-               \r
-               /* get next surface */\r
-               surface = (md3Surface_t*) ((picoByte_t*) surface + surface->ofsEnd);\r
-       }\r
-       \r
-       /* return the new pico model */\r
-       return picoModel;\r
-}\r
-\r
-\r
-\r
-/* pico file format module definition */\r
-const picoModule_t picoModuleMD3 =\r
-{\r
-       "1.3",                                          /* module version string */\r
-       "Quake 3 Arena",                        /* module display name */\r
-       "Randy Reddig",                         /* author's name */\r
-       "2002 Randy Reddig",            /* module copyright */\r
-       {\r
-               "md3", NULL, NULL, NULL /* default extensions to use */\r
-       },\r
-       _md3_canload,                           /* validation routine */\r
-       _md3_load,                                      /* load routine */\r
-        NULL,                                          /* save validation routine */\r
-        NULL                                           /* save routine */\r
-};\r
+/* -----------------------------------------------------------------------------
+
+   PicoModel Library
+
+   Copyright (c) 2002, Randy Reddig & seaw0lf
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without modification,
+   are permitted provided that the following conditions are met:
+
+   Redistributions of source code must retain the above copyright notice, this list
+   of conditions and the following disclaimer.
+
+   Redistributions in binary form must reproduce the above copyright notice, this
+   list of conditions and the following disclaimer in the documentation and/or
+   other materials provided with the distribution.
+
+   Neither the names of the copyright holders nor the names of its contributors may
+   be used to endorse or promote products derived from this software without
+   specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+   ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   ----------------------------------------------------------------------------- */
+
+/* dependencies */
+#include "picointernal.h"
+
+
+
+/* md3 model format */
+const char *MD3_MAGIC = "IDP3";
+const int MD3_VERSION = 15;
+
+/* md3 vertex scale */
+const float MD3_SCALE = ( 1.0f / 64.0f );
+
+/* md3 model frame information */
+typedef struct md3Frame_s
+{
+       float bounds[ 2 ][ 3 ];
+       float localOrigin[ 3 ];
+       float radius;
+       char creator[ 16 ];
+}
+md3Frame_t;
+
+/* md3 model tag information */
+typedef struct md3Tag_s
+{
+       char name[ 64 ];
+       float origin[ 3 ];
+       float axis[ 3 ][ 3 ];
+}
+md3Tag_t;
+
+/* md3 surface md3 (one object mesh) */
+typedef struct md3Surface_s
+{
+       char magic[ 4 ];
+       char name[ 64 ];            /* polyset name */
+       int flags;
+       int numFrames;              /* all model surfaces should have the same */
+       int numShaders;             /* all model surfaces should have the same */
+       int numVerts;
+       int numTriangles;
+       int ofsTriangles;
+       int ofsShaders;             /* offset from start of md3Surface_t */
+       int ofsSt;                  /* texture coords are common for all frames */
+       int ofsVertexes;            /* numVerts * numFrames */
+       int ofsEnd;                 /* next surface follows */
+}
+md3Surface_t;
+
+typedef struct md3Shader_s
+{
+       char name[ 64 ];
+       int shaderIndex;            /* for ingame use */
+}
+md3Shader_t;
+
+typedef struct md3Triangle_s
+{
+       int indexes[ 3 ];
+}
+md3Triangle_t;
+
+typedef struct md3TexCoord_s
+{
+       float st[ 2 ];
+}
+md3TexCoord_t;
+
+typedef struct md3Vertex_s
+{
+       short xyz[ 3 ];
+       short normal;
+}
+md3Vertex_t;
+
+
+/* md3 model file md3 structure */
+typedef struct md3_s
+{
+       char magic[ 4 ];            /* MD3_MAGIC */
+       int version;
+       char name[ 64 ];            /* model name */
+       int flags;
+       int numFrames;
+       int numTags;
+       int numSurfaces;
+       int numSkins;               /* number of skins for the mesh */
+       int ofsFrames;              /* offset for first frame */
+       int ofsTags;                /* numFrames * numTags */
+       int ofsSurfaces;            /* first surface, others follow */
+       int ofsEnd;                 /* end of file */
+}
+md3_t;
+
+
+
+
+/*
+   _md3_canload()
+   validates a quake3 arena md3 model file. btw, i use the
+   preceding underscore cause it's a static func referenced
+   by one structure only.
+ */
+
+static int _md3_canload( PM_PARAMS_CANLOAD ){
+       const md3_t *md3;
+
+
+       /* sanity check */
+       if ( (size_t) bufSize < ( sizeof( *md3 ) * 2 ) ) {
+               return PICO_PMV_ERROR_SIZE;
+       }
+
+       /* set as md3 */
+       md3 = (const md3_t*) buffer;
+
+       /* check md3 magic */
+       if ( *( (const int*) md3->magic ) != *( (const int*) MD3_MAGIC ) ) {
+               return PICO_PMV_ERROR_IDENT;
+       }
+
+       /* check md3 version */
+       if ( _pico_little_long( md3->version ) != MD3_VERSION ) {
+               return PICO_PMV_ERROR_VERSION;
+       }
+
+       /* file seems to be a valid md3 */
+       return PICO_PMV_OK;
+}
+
+
+
+/*
+   _md3_load()
+   loads a quake3 arena md3 model file.
+ */
+
+static picoModel_t *_md3_load( PM_PARAMS_LOAD ){
+       int i, j;
+       picoByte_t      *bb, *bb0;
+       md3_t           *md3;
+       md3Surface_t    *surface;
+       md3Shader_t     *shader;
+       md3TexCoord_t   *texCoord;
+       md3Frame_t      *frame;
+       md3Triangle_t   *triangle;
+       md3Vertex_t     *vertex;
+       double lat, lng;
+
+       picoModel_t     *picoModel;
+       picoSurface_t   *picoSurface;
+       picoShader_t    *picoShader;
+       picoVec3_t xyz, normal;
+       picoVec2_t st;
+
+
+       /* -------------------------------------------------
+          md3 loading
+          ------------------------------------------------- */
+
+
+       /* set as md3 */
+       bb0 = bb = (picoByte_t*) _pico_alloc( bufSize );
+       memcpy( bb, buffer, bufSize );
+       md3 = (md3_t*) bb;
+
+       /* check ident and version */
+       if ( *( (int*) md3->magic ) != *( (int*) MD3_MAGIC ) || _pico_little_long( md3->version ) != MD3_VERSION ) {
+               /* not an md3 file (todo: set error) */
+               _pico_free( bb0 );
+               return NULL;
+       }
+
+       /* swap md3; sea: swaps fixed */
+       md3->version = _pico_little_long( md3->version );
+       md3->numFrames = _pico_little_long( md3->numFrames );
+       md3->numTags = _pico_little_long( md3->numTags );
+       md3->numSurfaces = _pico_little_long( md3->numSurfaces );
+       md3->numSkins = _pico_little_long( md3->numSkins );
+       md3->ofsFrames = _pico_little_long( md3->ofsFrames );
+       md3->ofsTags = _pico_little_long( md3->ofsTags );
+       md3->ofsSurfaces = _pico_little_long( md3->ofsSurfaces );
+       md3->ofsEnd = _pico_little_long( md3->ofsEnd );
+
+       /* do frame check */
+       if ( md3->numFrames < 1 ) {
+               _pico_printf( PICO_ERROR, "MD3 with 0 frames" );
+               _pico_free( bb0 );
+               return NULL;
+       }
+
+       if ( frameNum < 0 || frameNum >= md3->numFrames ) {
+               _pico_printf( PICO_ERROR, "Invalid or out-of-range MD3 frame specified" );
+               _pico_free( bb0 );
+               return NULL;
+       }
+
+       /* swap frames */
+       frame = (md3Frame_t*) ( bb + md3->ofsFrames );
+       for ( i = 0; i < md3->numFrames; i++, frame++ )
+       {
+               frame->radius = _pico_little_float( frame->radius );
+               for ( j = 0; j < 3; j++ )
+               {
+                       frame->bounds[ 0 ][ j ] = _pico_little_float( frame->bounds[ 0 ][ j ] );
+                       frame->bounds[ 1 ][ j ] = _pico_little_float( frame->bounds[ 1 ][ j ] );
+                       frame->localOrigin[ j ] = _pico_little_float( frame->localOrigin[ j ] );
+               }
+       }
+
+       /* swap surfaces */
+       surface = (md3Surface_t*) ( bb + md3->ofsSurfaces );
+       for ( i = 0; i < md3->numSurfaces; i++ )
+       {
+               /* swap surface md3; sea: swaps fixed */
+               surface->flags = _pico_little_long( surface->flags );
+               surface->numFrames = _pico_little_long( surface->numFrames );
+               surface->numShaders = _pico_little_long( surface->numShaders );
+               surface->numTriangles = _pico_little_long( surface->numTriangles );
+               surface->ofsTriangles = _pico_little_long( surface->ofsTriangles );
+               surface->numVerts = _pico_little_long( surface->numVerts );
+               surface->ofsShaders = _pico_little_long( surface->ofsShaders );
+               surface->ofsSt = _pico_little_long( surface->ofsSt );
+               surface->ofsVertexes = _pico_little_long( surface->ofsVertexes );
+               surface->ofsEnd = _pico_little_long( surface->ofsEnd );
+
+               /* swap triangles */
+               triangle = (md3Triangle_t*) ( (picoByte_t*) surface + surface->ofsTriangles );
+               for ( j = 0; j < surface->numTriangles; j++, triangle++ )
+               {
+                       /* sea: swaps fixed */
+                       triangle->indexes[ 0 ] = _pico_little_long( triangle->indexes[ 0 ] );
+                       triangle->indexes[ 1 ] = _pico_little_long( triangle->indexes[ 1 ] );
+                       triangle->indexes[ 2 ] = _pico_little_long( triangle->indexes[ 2 ] );
+               }
+
+               /* swap st coords */
+               texCoord = (md3TexCoord_t*) ( (picoByte_t*) surface + surface->ofsSt );
+               for ( j = 0; j < surface->numVerts; j++, texCoord++ )
+               {
+                       texCoord->st[ 0 ] = _pico_little_float( texCoord->st[ 0 ] );
+                       texCoord->st[ 1 ] = _pico_little_float( texCoord->st[ 1 ] );
+               }
+
+               /* swap xyz/normals */
+               vertex = (md3Vertex_t*) ( (picoByte_t*) surface + surface->ofsVertexes );
+               for ( j = 0; j < ( surface->numVerts * surface->numFrames ); j++, vertex++ )
+               {
+                       vertex->xyz[ 0 ] = _pico_little_short( vertex->xyz[ 0 ] );
+                       vertex->xyz[ 1 ] = _pico_little_short( vertex->xyz[ 1 ] );
+                       vertex->xyz[ 2 ] = _pico_little_short( vertex->xyz[ 2 ] );
+                       vertex->normal   = _pico_little_short( vertex->normal );
+               }
+
+               /* get next surface */
+               surface = (md3Surface_t*) ( (picoByte_t*) surface + surface->ofsEnd );
+       }
+
+       /* -------------------------------------------------
+          pico model creation
+          ------------------------------------------------- */
+
+       /* create new pico model */
+       picoModel = PicoNewModel();
+       if ( picoModel == NULL ) {
+               _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
+               _pico_free( bb0 );
+               return NULL;
+       }
+
+       /* do model setup */
+       PicoSetModelFrameNum( picoModel, frameNum );
+       PicoSetModelNumFrames( picoModel, md3->numFrames ); /* sea */
+       PicoSetModelName( picoModel, fileName );
+       PicoSetModelFileName( picoModel, fileName );
+
+       /* md3 surfaces become picomodel surfaces */
+       surface = (md3Surface_t*) ( bb + md3->ofsSurfaces );
+
+       /* run through md3 surfaces */
+       for ( i = 0; i < md3->numSurfaces; i++ )
+       {
+               /* allocate new pico surface */
+               picoSurface = PicoNewSurface( picoModel );
+               if ( picoSurface == NULL ) {
+                       _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
+                       PicoFreeModel( picoModel ); /* sea */
+                       _pico_free( bb0 );
+                       return NULL;
+               }
+
+               /* md3 model surfaces are all triangle meshes */
+               PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
+
+               /* set surface name */
+               PicoSetSurfaceName( picoSurface, surface->name );
+
+               /* create new pico shader -sea */
+               picoShader = PicoNewShader( picoModel );
+               if ( picoShader == NULL ) {
+                       _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
+                       PicoFreeModel( picoModel );
+                       _pico_free( bb0 );
+                       return NULL;
+               }
+
+               /* detox and set shader name */
+               shader = (md3Shader_t*) ( (picoByte_t*) surface + surface->ofsShaders );
+               _pico_setfext( shader->name, NULL );
+               _pico_unixify( shader->name );
+               PicoSetShaderName( picoShader, shader->name );
+
+               /* associate current surface with newly created shader */
+               PicoSetSurfaceShader( picoSurface, picoShader );
+
+               /* copy indexes */
+               triangle = (md3Triangle_t *) ( (picoByte_t*) surface + surface->ofsTriangles );
+
+               for ( j = 0; j < surface->numTriangles; j++, triangle++ )
+               {
+                       PicoSetSurfaceIndex( picoSurface, ( j * 3 + 0 ), (picoIndex_t) triangle->indexes[ 0 ] );
+                       PicoSetSurfaceIndex( picoSurface, ( j * 3 + 1 ), (picoIndex_t) triangle->indexes[ 1 ] );
+                       PicoSetSurfaceIndex( picoSurface, ( j * 3 + 2 ), (picoIndex_t) triangle->indexes[ 2 ] );
+               }
+
+               /* copy vertexes */
+               texCoord = (md3TexCoord_t*) ( (picoByte_t *) surface + surface->ofsSt );
+               vertex = (md3Vertex_t*) ( (picoByte_t*) surface + surface->ofsVertexes + surface->numVerts * frameNum * sizeof( md3Vertex_t ) );
+
+               for ( j = 0; j < surface->numVerts; j++, texCoord++, vertex++ )
+               {
+                       /* set vertex origin */
+                       xyz[ 0 ] = MD3_SCALE * vertex->xyz[ 0 ];
+                       xyz[ 1 ] = MD3_SCALE * vertex->xyz[ 1 ];
+                       xyz[ 2 ] = MD3_SCALE * vertex->xyz[ 2 ];
+                       PicoSetSurfaceXYZ( picoSurface, j, xyz );
+
+                       /* decode lat/lng normal to 3 float normal */
+                       lat = (float) ( ( vertex->normal >> 8 ) & 0xff );
+                       lng = (float) ( vertex->normal & 0xff );
+                       lat *= PICO_PI / 128;
+                       lng *= PICO_PI / 128;
+                       normal[ 0 ] = (picoVec_t) cos( lat ) * (picoVec_t) sin( lng );
+                       normal[ 1 ] = (picoVec_t) sin( lat ) * (picoVec_t) sin( lng );
+                       normal[ 2 ] = (picoVec_t) cos( lng );
+                       PicoSetSurfaceNormal( picoSurface, j, normal );
+
+                       /* set st coords */
+                       st[ 0 ] = texCoord->st[ 0 ];
+                       st[ 1 ] = texCoord->st[ 1 ];
+                       PicoSetSurfaceST( picoSurface, 0, j, st );
+
+                       /* set color */
+                       PicoSetSurfaceColor( picoSurface, 0, j, picoColor_white );
+               }
+
+               /* get next surface */
+               surface = (md3Surface_t*) ( (picoByte_t*) surface + surface->ofsEnd );
+       }
+
+       /* return the new pico model */
+       _pico_free( bb0 );
+       return picoModel;
+}
+
+
+
+/* pico file format module definition */
+const picoModule_t picoModuleMD3 =
+{
+       "1.3",                      /* module version string */
+       "Quake 3 Arena",            /* module display name */
+       "Randy Reddig",             /* author's name */
+       "2002 Randy Reddig",        /* module copyright */
+       {
+               "md3", NULL, NULL, NULL /* default extensions to use */
+       },
+       _md3_canload,               /* validation routine */
+       _md3_load,                  /* load routine */
+       NULL,                       /* save validation routine */
+       NULL                        /* save routine */
+};