]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/picomodel/pm_lwo.c
set eol-style
[xonotic/netradiant.git] / libs / picomodel / pm_lwo.c
index ba83ceb5f6efd92c09ed2997ffc455409b1522f4..d7b3bc8afd489ce0251a99819c31d8de4b2bd15b 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
-/* marker */\r
-#define PM_LWO_C\r
-\r
-/* dependencies */\r
-#include "picointernal.h"\r
-#include "lwo/lwo2.h"\r
-\r
-/* uncomment when debugging this module */\r
-/*#define DEBUG_PM_LWO*/\r
-\r
-#ifdef DEBUG_PM_LWO\r
-#include "time.h"\r
-#endif\r
-\r
-/* helper functions */\r
-static const char *lwo_lwIDToStr( unsigned int lwID )\r
-{\r
-       static char lwIDStr[5];\r
-\r
-       if (!lwID)\r
-       {\r
-               return "n/a";\r
-       }\r
-\r
-       lwIDStr[ 0 ] = (char)((lwID) >> 24);\r
-       lwIDStr[ 1 ] = (char)((lwID) >> 16);\r
-       lwIDStr[ 2 ] = (char)((lwID) >> 8);\r
-       lwIDStr[ 3 ] = (char)((lwID));\r
-       lwIDStr[ 4 ] = '\0';\r
-\r
-       return lwIDStr;\r
-}\r
-\r
-/*\r
-_lwo_canload()\r
-validates a LightWave Object model file. btw, i use the\r
-preceding underscore cause it's a static func referenced\r
-by one structure only.\r
-*/\r
-static int _lwo_canload( PM_PARAMS_CANLOAD )\r
-{\r
-       picoMemStream_t *s;\r
-       unsigned int failID = 0;\r
-       int failpos = -1;\r
-       int ret;\r
-\r
-       /* create a new pico memorystream */\r
-       s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );\r
-       if (s == NULL)\r
-       {\r
-               return PICO_PMV_ERROR_MEMORY;\r
-       }\r
-\r
-       ret = lwValidateObject( fileName, s, &failID, &failpos );\r
-\r
-       _pico_free_memstream( s );\r
-\r
-       return ret;\r
-}\r
-\r
-/*\r
-_lwo_load()\r
-loads a LightWave Object model file.\r
-*/\r
-static picoModel_t *_lwo_load( PM_PARAMS_LOAD )\r
-{\r
-       picoMemStream_t *s;\r
-       unsigned int    failID = 0;\r
-       int                             failpos = -1;\r
-       lwObject                *obj;\r
-       lwSurface               *surface;\r
-       lwLayer                 *layer;\r
-       lwPoint                 *pt;\r
-       lwPolygon               *pol;\r
-       lwPolVert               *v;\r
-       lwVMapPt                *vm;\r
-       char                    name[ 64 ];\r
-       int                             i, j, k, numverts;\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
-       int                             defaultSTAxis[ 2 ];\r
-       picoVec2_t              defaultXYZtoSTScale;\r
-\r
-       picoVertexCombinationHash_t **hashTable;\r
-       picoVertexCombinationHash_t     *vertexCombinationHash;\r
-\r
-#ifdef DEBUG_PM_LWO\r
-       clock_t load_start, load_finish, convert_start, convert_finish;\r
-       double load_elapsed, convert_elapsed;\r
-\r
-       load_start = clock();\r
-#endif\r
-\r
-       /* do frame check */\r
-       if( frameNum < 0 || frameNum >= 1 )\r
-       {\r
-               _pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" );\r
-               return NULL;\r
-       }\r
-\r
-       /* create a new pico memorystream */\r
-       s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );\r
-       if (s == NULL)\r
-       {\r
-               return NULL;\r
-       }\r
-\r
-       obj = lwGetObject( fileName, s, &failID, &failpos );\r
-\r
-       _pico_free_memstream( s );\r
-\r
-       if( !obj ) {\r
-               _pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos );\r
-               return NULL;\r
-       }\r
-\r
-#ifdef DEBUG_PM_LWO\r
-       convert_start = load_finish = clock();\r
-       load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC;\r
-#endif\r
-\r
-       /* -------------------------------------------------\r
-       pico model creation\r
-       ------------------------------------------------- */\r
-       \r
-       /* create a 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, 1 );\r
-       PicoSetModelName( picoModel, fileName );\r
-       PicoSetModelFileName( picoModel, fileName );\r
-\r
-       /* create all polygons from layer[ 0 ] that belong to this surface */\r
-       layer = &obj->layer[0];\r
-\r
-       /* warn the user that other layers are discarded */\r
-       if (obj->nlayers > 1)\r
-       {\r
-               _pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers );\r
-       }\r
-\r
-       /* initialize dummy normal */\r
-       normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f;\r
-\r
-       /* setup default st map */\r
-       st[ 0 ] = st[ 1 ] = 0.f;        /* st[0] holds max, st[1] holds max par one */\r
-       defaultSTAxis[ 0 ] = 0;\r
-       defaultSTAxis[ 1 ] = 1;\r
-       for( i = 0; i < 3; i++ )\r
-       {\r
-               float min = layer->bbox[ i ];\r
-               float max = layer->bbox[ i + 3 ];\r
-               float size = max - min;\r
-               \r
-               if (size > st[ 0 ])\r
-               {\r
-                       defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ];\r
-                       defaultSTAxis[ 0 ] = i;\r
-\r
-                       st[ 1 ] = st[ 0 ];\r
-                       st[ 0 ] = size;\r
-               }\r
-               else if (size > st[ 1 ])\r
-               {\r
-                       defaultSTAxis[ 1 ] = i;\r
-                       st[ 1 ] = size;\r
-               }\r
-       }\r
-       defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ];\r
-       defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ];\r
-\r
-       /* LWO surfaces become pico surfaces */\r
-       surface = obj->surf;\r
-       while (surface)\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 );\r
-                       lwFreeObject( obj );\r
-                       return NULL;\r
-               }\r
-\r
-               /* LWO 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 */\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
-                       lwFreeObject( obj );\r
-                       return NULL;\r
-               }\r
-\r
-               /* detox and set shader name */\r
-               strncpy( name, surface->name, sizeof(name) );\r
-               _pico_setfext( name, "" );\r
-               _pico_unixify( name );\r
-               PicoSetShaderName( picoShader, name );\r
-\r
-               /* associate current surface with newly created shader */\r
-               PicoSetSurfaceShader( picoSurface, picoShader );\r
-\r
-               /* copy indices and vertex data */\r
-               numverts = 0;\r
-\r
-               hashTable = PicoNewVertexCombinationHashTable();\r
-\r
-               if (hashTable == NULL)\r
-               {\r
-                       _pico_printf( PICO_ERROR, "Unable to allocate hash table" );\r
-                       PicoFreeModel( picoModel );\r
-                       lwFreeObject( obj );\r
-                       return NULL;\r
-               }\r
-\r
-               for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )\r
-               {\r
-                       /* does this polygon belong to this surface? */\r
-                       if (pol->surf != surface)\r
-                               continue;\r
-\r
-                       /* we only support polygons of the FACE type */\r
-                       if (pol->type != ID_FACE)\r
-                       {\r
-                               _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );\r
-                               continue;\r
-                       }\r
-\r
-                       /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */\r
-                       if (pol->nverts != 3)\r
-                       {\r
-                               _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );\r
-                               continue;\r
-                       }\r
-\r
-                       for( j = 0, v = pol->v; j < 3; j++, v++ )\r
-                       {\r
-                               pt = &layer->point.pt[ v->index ];\r
-\r
-                               /* setup data */\r
-                               xyz[ 0 ] = pt->pos[ 0 ];\r
-                               xyz[ 1 ] = pt->pos[ 2 ];\r
-                               xyz[ 2 ] = pt->pos[ 1 ];\r
-\r
-                               normal[ 0 ] = v->norm[ 0 ];\r
-                               normal[ 1 ] = v->norm[ 2 ];\r
-                               normal[ 2 ] = v->norm[ 1 ];\r
-\r
-                               st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];\r
-                               st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];\r
-\r
-                               color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);\r
-                               color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);\r
-                               color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);\r
-                               color[ 3 ] = 0xFF;\r
-\r
-                               /* set from points */\r
-                               for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )\r
-                               {\r
-                                       if (vm->vmap->type == LWID_('T','X','U','V'))\r
-                                       {\r
-                                               /* set st coords */\r
-                                               st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];\r
-                                               st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];\r
-                                       }\r
-                                       else if (vm->vmap->type == LWID_('R','G','B','A'))\r
-                                       {\r
-                                               /* set rgba */\r
-                                               color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);\r
-                                               color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);\r
-                                               color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);\r
-                                               color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);\r
-                                       }\r
-                               }\r
-\r
-                               /* override with polygon data */\r
-                               for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )\r
-                               {\r
-                                       if (vm->vmap->type == LWID_('T','X','U','V'))\r
-                                       {\r
-                                               /* set st coords */\r
-                                               st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];\r
-                                               st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];\r
-                                       }\r
-                                       else if (vm->vmap->type == LWID_('R','G','B','A'))\r
-                                       {\r
-                                               /* set rgba */\r
-                                               color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);\r
-                                               color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);\r
-                                               color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);\r
-                                               color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);\r
-                                       }\r
-                               }\r
-\r
-                               /* find vertex in this surface and if we can't find it there create it */\r
-                               vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );\r
-\r
-                               if (vertexCombinationHash)\r
-                               {\r
-                                       /* found an existing one */\r
-                                       PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index );\r
-                               }\r
-                               else\r
-                               {\r
-                                       /* it is a new one */\r
-                                       vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );\r
-\r
-                                       if (vertexCombinationHash == NULL)\r
-                                       {\r
-                                               _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );\r
-                                               PicoFreeVertexCombinationHashTable( hashTable );\r
-                                               PicoFreeModel( picoModel );\r
-                                               lwFreeObject( obj );\r
-                                               return NULL;\r
-                                       }\r
-\r
-                                       /* add the vertex to this surface */\r
-                                       PicoSetSurfaceXYZ( picoSurface, numverts, xyz );\r
-\r
-                                       /* set dummy normal */\r
-                                       PicoSetSurfaceNormal( picoSurface, numverts, normal );\r
-\r
-                                       /* set color */\r
-                                       PicoSetSurfaceColor( picoSurface, 0, numverts, color );\r
-\r
-                                       /* set st coords */\r
-                                       PicoSetSurfaceST( picoSurface, 0, numverts, st );\r
-\r
-                                       /* set index */\r
-                                       PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts );\r
-\r
-                                       numverts++;\r
-                               }\r
-                       }\r
-               }\r
-\r
-               /* free the hashtable */\r
-               PicoFreeVertexCombinationHashTable( hashTable );\r
-\r
-               /* get next surface */          \r
-               surface = surface->next;\r
-       }\r
-\r
-#ifdef DEBUG_PM_LWO\r
-       load_start = convert_finish = clock();\r
-#endif\r
-\r
-       lwFreeObject( obj );\r
-\r
-#ifdef DEBUG_PM_LWO\r
-       load_finish = clock();\r
-       load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC;\r
-       convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC;\r
-       _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed  );\r
-#endif\r
-\r
-       /* return the new pico model */\r
-       return picoModel;\r
-}\r
-\r
-/* pico file format module definition */\r
-const picoModule_t picoModuleLWO =\r
-{\r
-       "1.0",                                          /* module version string */\r
-       "LightWave Object",                     /* module display name */\r
-       "Arnout van Meer",                      /* author's name */\r
-       "2003 Arnout van Meer, 2000 Ernie Wright",              /* module copyright */\r
-       {\r
-               "lwo", NULL, NULL, NULL /* default extensions to use */\r
-       },\r
-       _lwo_canload,                           /* validation routine */\r
-       _lwo_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.
+
+----------------------------------------------------------------------------- */
+
+/* marker */
+#define PM_LWO_C
+
+/* dependencies */
+#include "picointernal.h"
+#include "lwo/lwo2.h"
+
+/* uncomment when debugging this module */
+/*#define DEBUG_PM_LWO*/
+
+#ifdef DEBUG_PM_LWO
+#include "time.h"
+#endif
+
+/* helper functions */
+static const char *lwo_lwIDToStr( unsigned int lwID )
+{
+       static char lwIDStr[5];
+
+       if (!lwID)
+       {
+               return "n/a";
+       }
+
+       lwIDStr[ 0 ] = (char)((lwID) >> 24);
+       lwIDStr[ 1 ] = (char)((lwID) >> 16);
+       lwIDStr[ 2 ] = (char)((lwID) >> 8);
+       lwIDStr[ 3 ] = (char)((lwID));
+       lwIDStr[ 4 ] = '\0';
+
+       return lwIDStr;
+}
+
+/*
+_lwo_canload()
+validates a LightWave Object model file. btw, i use the
+preceding underscore cause it's a static func referenced
+by one structure only.
+*/
+static int _lwo_canload( PM_PARAMS_CANLOAD )
+{
+       picoMemStream_t *s;
+       unsigned int failID = 0;
+       int failpos = -1;
+       int ret;
+
+       /* create a new pico memorystream */
+       s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
+       if (s == NULL)
+       {
+               return PICO_PMV_ERROR_MEMORY;
+       }
+
+       ret = lwValidateObject( fileName, s, &failID, &failpos );
+
+       _pico_free_memstream( s );
+
+       return ret;
+}
+
+/*
+_lwo_load()
+loads a LightWave Object model file.
+*/
+static picoModel_t *_lwo_load( PM_PARAMS_LOAD )
+{
+       picoMemStream_t *s;
+       unsigned int    failID = 0;
+       int                             failpos = -1;
+       lwObject                *obj;
+       lwSurface               *surface;
+       lwLayer                 *layer;
+       lwPoint                 *pt;
+       lwPolygon               *pol;
+       lwPolVert               *v;
+       lwVMapPt                *vm;
+       char                    name[ 64 ];
+       int                             i, j, k, numverts;
+
+       picoModel_t             *picoModel;
+       picoSurface_t   *picoSurface;
+       picoShader_t    *picoShader;
+       picoVec3_t              xyz, normal;
+       picoVec2_t              st;
+       picoColor_t             color;
+
+       int                             defaultSTAxis[ 2 ];
+       picoVec2_t              defaultXYZtoSTScale;
+
+       picoVertexCombinationHash_t **hashTable;
+       picoVertexCombinationHash_t     *vertexCombinationHash;
+
+#ifdef DEBUG_PM_LWO
+       clock_t load_start, load_finish, convert_start, convert_finish;
+       double load_elapsed, convert_elapsed;
+
+       load_start = clock();
+#endif
+
+       /* do frame check */
+       if( frameNum < 0 || frameNum >= 1 )
+       {
+               _pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" );
+               return NULL;
+       }
+
+       /* create a new pico memorystream */
+       s = _pico_new_memstream( (picoByte_t *)buffer, bufSize );
+       if (s == NULL)
+       {
+               return NULL;
+       }
+
+       obj = lwGetObject( fileName, s, &failID, &failpos );
+
+       _pico_free_memstream( s );
+
+       if( !obj ) {
+               _pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos );
+               return NULL;
+       }
+
+#ifdef DEBUG_PM_LWO
+       convert_start = load_finish = clock();
+       load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC;
+#endif
+
+       /* -------------------------------------------------
+       pico model creation
+       ------------------------------------------------- */
+       
+       /* create a new pico model */
+       picoModel = PicoNewModel();
+       if (picoModel == NULL)
+       {
+               _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
+               return NULL;
+       }
+
+       /* do model setup */
+       PicoSetModelFrameNum( picoModel, frameNum );
+       PicoSetModelNumFrames( picoModel, 1 );
+       PicoSetModelName( picoModel, fileName );
+       PicoSetModelFileName( picoModel, fileName );
+
+       /* create all polygons from layer[ 0 ] that belong to this surface */
+       layer = &obj->layer[0];
+
+       /* warn the user that other layers are discarded */
+       if (obj->nlayers > 1)
+       {
+               _pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers );
+       }
+
+       /* initialize dummy normal */
+       normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f;
+
+       /* setup default st map */
+       st[ 0 ] = st[ 1 ] = 0.f;        /* st[0] holds max, st[1] holds max par one */
+       defaultSTAxis[ 0 ] = 0;
+       defaultSTAxis[ 1 ] = 1;
+       for( i = 0; i < 3; i++ )
+       {
+               float min = layer->bbox[ i ];
+               float max = layer->bbox[ i + 3 ];
+               float size = max - min;
+               
+               if (size > st[ 0 ])
+               {
+                       defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ];
+                       defaultSTAxis[ 0 ] = i;
+
+                       st[ 1 ] = st[ 0 ];
+                       st[ 0 ] = size;
+               }
+               else if (size > st[ 1 ])
+               {
+                       defaultSTAxis[ 1 ] = i;
+                       st[ 1 ] = size;
+               }
+       }
+       defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ];
+       defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ];
+
+       /* LWO surfaces become pico surfaces */
+       surface = obj->surf;
+       while (surface)
+       {
+               /* allocate new pico surface */
+               picoSurface = PicoNewSurface( picoModel );
+               if (picoSurface == NULL)
+               {
+                       _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
+                       PicoFreeModel( picoModel );
+                       lwFreeObject( obj );
+                       return NULL;
+               }
+
+               /* LWO model surfaces are all triangle meshes */
+               PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
+
+               /* set surface name */
+               PicoSetSurfaceName( picoSurface, surface->name );
+
+               /* create new pico shader */
+               picoShader = PicoNewShader( picoModel );
+               if (picoShader == NULL)
+               {
+                       _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
+                       PicoFreeModel( picoModel );
+                       lwFreeObject( obj );
+                       return NULL;
+               }
+
+               /* detox and set shader name */
+               strncpy( name, surface->name, sizeof(name) );
+               _pico_setfext( name, "" );
+               _pico_unixify( name );
+               PicoSetShaderName( picoShader, name );
+
+               /* associate current surface with newly created shader */
+               PicoSetSurfaceShader( picoSurface, picoShader );
+
+               /* copy indices and vertex data */
+               numverts = 0;
+
+               hashTable = PicoNewVertexCombinationHashTable();
+
+               if (hashTable == NULL)
+               {
+                       _pico_printf( PICO_ERROR, "Unable to allocate hash table" );
+                       PicoFreeModel( picoModel );
+                       lwFreeObject( obj );
+                       return NULL;
+               }
+
+               for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ )
+               {
+                       /* does this polygon belong to this surface? */
+                       if (pol->surf != surface)
+                               continue;
+
+                       /* we only support polygons of the FACE type */
+                       if (pol->type != ID_FACE)
+                       {
+                               _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) );
+                               continue;
+                       }
+
+                       /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */
+                       if (pol->nverts != 3)
+                       {
+                               _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts );
+                               continue;
+                       }
+
+                       for( j = 0, v = pol->v; j < 3; j++, v++ )
+                       {
+                               pt = &layer->point.pt[ v->index ];
+
+                               /* setup data */
+                               xyz[ 0 ] = pt->pos[ 0 ];
+                               xyz[ 1 ] = pt->pos[ 2 ];
+                               xyz[ 2 ] = pt->pos[ 1 ];
+
+                               normal[ 0 ] = v->norm[ 0 ];
+                               normal[ 1 ] = v->norm[ 2 ];
+                               normal[ 2 ] = v->norm[ 1 ];
+
+                               st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ];
+                               st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ];
+
+                               color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
+                               color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
+                               color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
+                               color[ 3 ] = 0xFF;
+
+                               /* set from points */
+                               for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ )
+                               {
+                                       if (vm->vmap->type == LWID_('T','X','U','V'))
+                                       {
+                                               /* set st coords */
+                                               st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
+                                               st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
+                                       }
+                                       else if (vm->vmap->type == LWID_('R','G','B','A'))
+                                       {
+                                               /* set rgba */
+                                               color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
+                                               color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
+                                               color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
+                                               color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
+                                       }
+                               }
+
+                               /* override with polygon data */
+                               for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ )
+                               {
+                                       if (vm->vmap->type == LWID_('T','X','U','V'))
+                                       {
+                                               /* set st coords */
+                                               st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ];
+                                               st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ];
+                                       }
+                                       else if (vm->vmap->type == LWID_('R','G','B','A'))
+                                       {
+                                               /* set rgba */
+                                               color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF);
+                                               color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF);
+                                               color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF);
+                                               color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF);
+                                       }
+                               }
+
+                               /* find vertex in this surface and if we can't find it there create it */
+                               vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color );
+
+                               if (vertexCombinationHash)
+                               {
+                                       /* found an existing one */
+                                       PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index );
+                               }
+                               else
+                               {
+                                       /* it is a new one */
+                                       vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts );
+
+                                       if (vertexCombinationHash == NULL)
+                                       {
+                                               _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" );
+                                               PicoFreeVertexCombinationHashTable( hashTable );
+                                               PicoFreeModel( picoModel );
+                                               lwFreeObject( obj );
+                                               return NULL;
+                                       }
+
+                                       /* add the vertex to this surface */
+                                       PicoSetSurfaceXYZ( picoSurface, numverts, xyz );
+
+                                       /* set dummy normal */
+                                       PicoSetSurfaceNormal( picoSurface, numverts, normal );
+
+                                       /* set color */
+                                       PicoSetSurfaceColor( picoSurface, 0, numverts, color );
+
+                                       /* set st coords */
+                                       PicoSetSurfaceST( picoSurface, 0, numverts, st );
+
+                                       /* set index */
+                                       PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts );
+
+                                       numverts++;
+                               }
+                       }
+               }
+
+               /* free the hashtable */
+               PicoFreeVertexCombinationHashTable( hashTable );
+
+               /* get next surface */          
+               surface = surface->next;
+       }
+
+#ifdef DEBUG_PM_LWO
+       load_start = convert_finish = clock();
+#endif
+
+       lwFreeObject( obj );
+
+#ifdef DEBUG_PM_LWO
+       load_finish = clock();
+       load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC;
+       convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC;
+       _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed  );
+#endif
+
+       /* return the new pico model */
+       return picoModel;
+}
+
+/* pico file format module definition */
+const picoModule_t picoModuleLWO =
+{
+       "1.0",                                          /* module version string */
+       "LightWave Object",                     /* module display name */
+       "Arnout van Meer",                      /* author's name */
+       "2003 Arnout van Meer, 2000 Ernie Wright",              /* module copyright */
+       {
+               "lwo", NULL, NULL, NULL /* default extensions to use */
+       },
+       _lwo_canload,                           /* validation routine */
+       _lwo_load,                                      /* load routine */
+        NULL,                                          /* save validation routine */
+        NULL                                           /* save routine */
+};